Next.js prerendering error on getStaticPaths

Viewed 486

I am building a Next.js app. Here is my file structure:

cpanearme
 -components
   -listitem.js
 -pages
    -home
       -index.js
     -firm
        -[id].js

Here is the code for a list item that pushes to the dynamic route:

     <a className='lead'
      onClick={() => {
        router.push({
          pathname: `/firm/${firm.id}`,
          query: { data: firm.id},
        })
      }}
    >View Their Profile and Reviews</a>   

Here is the entire dynamic page

import { withRouter } from 'next/router'
import Image from 'next/image'
import Link from 'next/link'
import StickyNavbar from '../../components/StickyNavbar'
import ReviewItem from "../../components/ReviewItem"
import Footer from '../../components/Footer'
import path from "path"
import fs from "fs"

const Profile = (props) => {
  const firm = props.firms[0].filter(firm => firm.id === props.router.query.id)[0]

    return (
        <div>
            <StickyNavbar/>
            <div className='container ' style={{backgroundColor:'#b0bec5'}}>
                <h3 className='text-primary'>{firm.name}</h3>
                <i>Founded {firm.years} ago</i>
     
                <div className="grid-3a">
                    <div className="card p-3">
                        Conact Info:<br/>  
                        {firm.address} <br/>
                        {firm.city}, {firm.state} <br/>
                        {firm.email} <br/>
                        {firm.phone}<br/>
                    </div>
                    <div>
                        <h3 className='lead text-primary text-center'>About this company:</h3><br/>
                        <p style={{textIndent:'10px'}} className='py-1'>{firm.profileCopy}</p>
                      </div>
                      <div>
                          <br/>
                          <br/>
                         On The Web: <br/>
                         <a href={firm.website} className='lead' >Their Site</a> <br/> 
                         <a href={firm.bbb} className='lead' >Their BBB</a>  
                      </div>
                </div>
                <br/>
                <div > <h3 className='text-center text-primary lead'> Meet The CPA </h3><br/>
                       <h4 className='text-center text-secondary lead'>{firm.cpa}</h4>
                        <div className='all-center round-img'>
                            <Image
            src={firm.cpapic}
            alt='Tax Expert CPA'
            className='round-img'
              width= "100px"
              height= "100px"
         
 
          /> </div> 
          <div className="mx-3">
              <p style={{textIndent:'10px'}}>{firm.cpabio}</p>
              </div>   
                
                </div>

        <br/>
        <br/>
                <div>
                   <h3 className='text-center text-primary lead'>Pros and Cons</h3>
                    <div className="grid-2">
                        <div className='card all-center'>Pros: <br/>
                        <ul>{firm.pros.map(pro=> (<li><i class="fas fa-plus"></i>{' '}{pro}</li>))}</ul>
                        </div>
                        <div className='card all-center'>Cons: <br/>
                        <ul>{firm.cons.map(con=> (<li><i class="fas fa-minus"></i>{' '}{con}</li>))}</ul>
                        </div>
                    </div>
                    </div>
<br/>
<br/>
                 <div><h3 className='text-center text-primary lead'>Services offered</h3> 
                    <div className="grid-3">
                        <div className='card'>Federal Tax : <br/>
                        <ul>{firm.services.federal.map(fed=> (<li><i class="fas fa-check"></i>{' '}{fed}</li>))}</ul>
                        </div>
                        <div className='card'>State Tax: <br/>
                        <ul>{firm.services.state.map(state=> (<li><i class="fas fa-check"></i>{' '}{state}</li>))}</ul>
                        </div>
                        <div className='card'>Accounting: <br/>
                        <ul>{firm.services.accounting.map(account=> (<li><i class="fas fa-check"></i>{' '}{account}</li>))}</ul>
                        </div>
                    </div>
                 </div>          
  <br/>
  <br/>
              <div>
                  <h3 className='text-center text-primary lead'>More About Their Fees:</h3>
                  
                    
   
                  <div className='text-center'> 
                  <h3>Pricing Information</h3>         
                     Minimum Debt: {firm.minimum} <br/>
                     Average Fees: {firm.cost} <br/>
                     Exploration Cost: {firm.exploration} <br/>
                     Average Client Savings: {firm.avgsavings}</div>
                
                  <p style={{textIndent:'10px'}}>{firm.fees}</p>
              </div>
<br/>
 <br/>                         <div>
                  <h3 className='text-center text-primary lead'>Read Real Customer Reviews:</h3>
                  <div className='all-center'>
                  {firm.reviews.map((review,i)=> (<ReviewItem review={review} key={i}/>))}
                  </div>
              </div>
            </div> 
            <Footer/>
        </div>
    )
  }

export default withRouter(Profile)

export async function getStaticPaths() {

    const postsDirectory = path.join(process.cwd(), 'firms')
    const filenames = fs.readdirSync(postsDirectory)

    const firms = filenames.map((filename) => {
    const filePath = path.join(postsDirectory, filename)
    const firms =  JSON.parse(fs.readFileSync(filePath, 'utf8'))

    // Generally you would parse/transform the contents
    // For example you can transform markdown to HTML here

    return firms
    
  })

  return {
   paths: firms[0].map(firm =>({ params: { id: firm.id}})), //firms[0].map(firm => { params: { id: firm.id}}),
   fallback: true // See the "fallback" section below
  };
}

export async function getStaticProps() {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const postsDirectory = path.join(process.cwd(), 'firms')
  const filenames = fs.readdirSync(postsDirectory)


  const firms = filenames.map((filename) => {
    const filePath = path.join(postsDirectory, filename)
    const firms =  JSON.parse(fs.readFileSync(filePath, 'utf8'))

    // Generally you would parse/transform the contents
    // For example you can transform markdown to HTML here

    return firms
    
  })

  
  // By returning { props: posts }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      firms,
    },
  }
}

I am in the build phase and I am running into the following error after setting up getStaticPaths. I can't figure out how to Google this exact mistake.

Error: Export encountered errors on following paths:
        /firm/[id]
        /taxfaqs/[id]

It successfully pre-renders everything that is passed with get static props but I don't know how to tell build that [id] isn't a thing that needs to get checked.

Is it [...id]?

1 Answers

There seems to be some confusion around how getStaticPaths and getStaticProps work.

getStaticPaths is used, in dynamic routes (like your /firm/[id]), to define the list of paths that have to be rendered to HTML at build time. Meaning you need to return an array of the possible paths for that route from it.

// Generates `firm/firm1` and `firm/firm2` paths at build time
export async function getStaticPaths() {
    return {
        paths: [
            { params: { id: 'firm1' } },
            { params: { id: 'firm2' } }
        ],
        fallback: true
    };
}

getStaticProps will then generate the props for each page based on the paths returned by getStaticPaths at build time.

// Generates the props for given path at build time
export async function getStaticProps({ params }) {
    // Add your own logic to retrieve the data for given path and pass it to `firm`
    // If the route is like `/firm/firm1`, then params.id is `firm1`
    return {
        props: {
            firm
        }
    };
}

Finally, in your page component you can access the data passed from the props.

const Profile = ({ firm }) => {
    // `firm` will contain the data for a given path
}
Related