DEV Community

Ali Sherief
Ali Sherief

Posted on

Exploring NextJS Features

If you read and followed the instructions in the previous post in this series, then you created a NextJS web app, though you might have already created a NextJS app before this, some other way. In this post I'm going to compare the method of writing a Next app with writing a React app.

Semantic differences between Next and React

The first thing you should know is that if you make a folder called /pages in your project, NextJS handles the routing for you for components in that folder, by looking at the file name of your component and naming the path accordingly. So, a file called /pages/profile.ts in your project will be accessible at the /profile path.

You can also have changing names for the paths, using a technique called dynamic routing. Dynamic routing in NextJS is when you create a file called /pages/posts/[someID].ts, you can access paths such as /posts/123 and /posts/abc. Whatever name someID you put will be accessible from useRouter().query, and useRouter comes from a NextJS package.

This is how you use useRouter in practice:

// file: /pages/posts/[someID].js
import { useRouter } from 'next/router'

const Post = () => {
  const router = useRouter()
  const { someID } = router.query

  return <p>Post: {someID}</p>
}

export default Post
Enter fullscreen mode Exit fullscreen mode

Now that you programmatically retrieved the value of the ID, you can conditionally render the appropriate content using it.

Also, you can pass query parameters of the form /somepath/replaced-id?key=value into NextJS apps using dynamic routing, you use the same query method that was used in the sample, but it will be an object that looks something like { "foo": "bar", "pid": "abc" }.

This is also what happens if you substitute an ID in a route below another route that is also a substituted ID, such as /pages/post/[pid]/[comment].js --> /post/abc/a-comment, and its query object will be:

{ "pid": "abc", "comment": "a-comment" }
Enter fullscreen mode Exit fullscreen mode

There is even a mechanism to capture all the child paths of a route using a single file name. Using the blog post example again, if I make a file in my project called /pages/post/[...someID].js, it will handle all routes from /posts/a, /posts/foobar, /posts/foobar/baz and others. By doing this, you have to render the appropriate page for each path.

Since static routes have a higher priority than dynamic routes, this is an excellent way to implement 404 pages; Just put a file called /pages/[notFound].js or any other name instead of notFound.

And then there is data fetching. This handy feature allows you retrieve state from a server before rendering the page. In plain React there is no intuitive way that I know of to make a request and collect the data before the page renders, so the fact that NextJS made it this easy to get state gives it a major advantage.

It can only get data though, not send data to a server.

When a component is built, NextJS immediately calls the exported function getStaticProps() defined in the component. Use it to fetch your props from a server, and then return an object containing a props key that has all your props inside. Here is an example of a typical getStaticProps function:

// Blog component omitted...

// This function gets called at build time
// It's defined at the top level of a file, assuming you are using React hooks.
export async function getStaticProps() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

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

There is also a function that fetches the ID, the one inside [...someID], of the page to render called getStaticPaths, and it's defined similarly to getStaticProps.

// This function gets called at build time
export async function getStaticPaths() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => `/posts/${post.id}`)

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}
Enter fullscreen mode Exit fullscreen mode

Basically what happens if you don't use this is that for each /post/1, /post/2 and such, it will render the same page on all of them which might not be what you desire.

In the above example, there is a fallback parameter which can be true or false, When it's false, paths not returned by getStaticPaths make a 404 error. When it's true, then those paths don't 404, instead a special flag is set that loads a fallback version of the page with no props defined. This enables you to render a fallback version of a page.

In the below snippet, a fallback page is displayed while getStaticProps is being run, until it finishes running, then props will be defined and the normal page is rendered. This requires that getStaticPaths returns fallback: true.

function Post({ post }) {
  const router = useRouter()

  // If the page is not yet generated, this will be displayed
  // initially until getStaticProps() finishes running
  if (router.isFallback) {
    return <div>Loading...</div>
  }

  // Render post...
}
Enter fullscreen mode Exit fullscreen mode

That's all folks

We reached the end of the NextJS overview. If you see any errors, please let me know so I can correct them.

Discussion (0)