DEV Community

Cover image for Next.js: the new normal
Shihabudheen US
Shihabudheen US

Posted on • Updated on

Next.js: the new normal

Next.js is a full-stack framework based on React.js.

What it offers:

  • Prerendering: the entire HTML is created in the server and send to the client. So the client receives the HTML rather than the JS file. Once the HTML(string) is available it gets rehydrated at the client side. One can think of rehydration as adding event listeners and making it interactive. All the routes are pre-rendered by default.
Scripts

The common commands used to run and build a next project are the following.

"scripts":{
  "dev": "next",   // start in dev mode
  "build": "next build", // start production build
  "start": "next start" // run production build
}
Enter fullscreen mode Exit fullscreen mode

Routing

  • using reach-router under the hood

  • file-system-based routing

  • for that, we create a special folder called pages

  • all the folder/file names become the routes for those files

  • we can handle dynamic routes and receive the parameters like notes/:id. For that we just need to create a file named [id].js(x) inside a notes folder. If the notes folder has an index file it will be treated as the notes/ route

  • to use the param inside the render function we can use useRouter hook from next/router. For classes, you have withRouter HOC.

notes/[id].js

import { useRouter } from 'next/router'

function App(){
 const router = useRouter()
 const {id} = router.query

 return (
    ...
 )
}

export default App
Enter fullscreen mode Exit fullscreen mode

note: In React, functional components are actually the render function. The entire function is the render method in case of functional components. With classes, we will have an explicit render() with a return value.

  • if you want to handle slugs, like notes/shihab/1, you can have a file named [...slug].js inside the notes directory. This time the router query will return an array-like ['shihab',1]. Even with catch-all routes, the index will still be used.

Navigation

Link component

For navigation next/link expose a Link element. It is always for client-side routing. That means, on navigation, this will not trigger a network call.

import Link from 'next/link'

function App(){
  ...
  return {
    ....
    <Link href='/notes/[id]' as={`/notes/${id}`}>
      <a>Note</a>
    </Link>
  }
}
Enter fullscreen mode Exit fullscreen mode

as path will be the exact path URL, the href will be the file's relative location. The href prop takes a page name as it is in the pages directory. For dynamic routes, you will need the as prop as well.

You must have an a tag as the child of the Link component, but the href lives on the Link.

For server-side routing, you can readily use an anchor tag like <a href='/docs'>Server side routing</a>

Programmatic routing

In order to navigate from code, one can use router.push() from next/router's useRouter hook.

import { useRouter } from 'next/router'

function naviagteOnSuccess(){
 const router = useRouter()

 ....
 router.push('/notes/[id]',`/notes/${note.id}`)
}
Enter fullscreen mode Exit fullscreen mode

Styling

  • if you are using global CSS, pages/_app.js is the only place you can import it. If you try to import it in other places Next.js will throw an error. This is more tied to the bundling of styles and loading them

  • Next.js readily supports CSS Modules. With CSS modules we get file scoped styles. How it works is, with each import of CSS module file, a file specific class name gets added(prepended) to the classes you use. So the style you use is specific to that particular file and doesn't collide with others. The CSS modules will only work with non-pure selectors like classes and ids, etc and not with element selectors(div, span, p,...). The filename should be like file-name.module.(s)css.

Special files

_app.js

  • if you want to hijack the entry file of Next, _app.js file is the place. If you want to inject global styles, props or anything, it should happen here. This _app.js is automatically created for you out of the box if you don't.

Next.js config

  • next-config.js in the root of the project

TS support

  • Just create a .tsconfig.json in the root.
  • Next will ask you to add some libs and dependencies. Add them.
  • Bhoom, now Next will auto-populate the tsconfig for you. No more traction in setting up TS.

API routes

  • Next is a full-stack framework. You can have your API route handlers inside a directory pages/api.
  • The routing is the same as that for pages.

Data fetching

  • by default fetch is available

Data can be fetched on the server and the client. Client-side data fetch is the same, what we do in a normal React app. The components may be rendered in the Server, but data fetching will only happen on the client in this case. That means, if you fetch the data in the client(using hooks or lifecycle methods), they aren't triggered on Server. The Server will render the view with the components initial State, that's all. No, waiting until the client fetches or manipulation is over.

To fetch data on the server we have

  • getStaticProps
  • getStaticPaths
  • getServerSideProps
  • getInitialProps

  • All of the above methods are only meant to run on the server(except getInitialProps, during subsequent calls).

  • they are not even added to the client bundle

  • these methods can access DB, file system and all the things that can be done on the server-side

  • the return value(objects) of these methods are injected into or send to the client-side components as JSON files

getStaticProps

  • to pass down any static props to the components, that are available during the build time
  • it may receive the props from the getStaticPaths method
  • the return value is always an object
  • this object is available as the props inside the component
  • when building dynamic pages you will have the params passed from getStaticPaths, inside the getStaticProps
  • it is only called once at the build time (when building the app using next build command)
export async function getStaticProps(context) {
  return {
    props: {}
  }
}
Enter fullscreen mode Exit fullscreen mode

getStaticPaths

  • if you want to generate static pages you can use this method
  • it should return an array of paths
  • the pages are created for the paths at build time
  • if the pages need some data to be fetched, we use the getStaticProps
  • it might not be required to statically generate all the pages in advance, so you can opt for runtime SSR using fallback: true
  • by using fallback you can show some loaders if required when the page is being built
export async function getStaticPaths() {
  // get all the paths for your posts from an API
  // or file system
  const results = await fetch('/api/posts')
  const posts = await results.json()
  // create the paths array
  const paths = posts.map(post => ({params: {slug: 
  post.slug}}))
  /*
  [
    {params: {slug: 'get-started-with-node'}},
    {params: {slug: 'top-frameworks'}}
  ]
  */
  return {paths}
}

export async function getStaticProps({ params }) {
  const res = await fetch(`/api/post/${params.slug}`)
  const post = await res.json()
  return {
    props: {post}
  }
}
Enter fullscreen mode Exit fullscreen mode

getServerSideProps

  • called on every request on the server
  • used if you want to do some data fetching for dynamic SSR routes
  • you will have access to HTTP header, query params,req and res headers
  • even if it is client-side navigation, this method is triggered on the server-side and data is sent down. This is actually an extra roundtrip ๐Ÿ˜ข.
export async function getServerSideProps() {
  const response = await fetch(`https://somedata.com`)
  const data = await response.json()

  return { props: { data } }
}
Enter fullscreen mode Exit fullscreen mode

getInitialProps

  • not recommended as per docs, but not yet deprecated ๐Ÿ’ช
  • on Server-Side Rendering(SSR) pages it is run on the server and data is passed down as JSON
  • for Client-Side Rendering(CSR) it runs on the client
  • used to fetch data

Note: when the page is fetched upon URL/address bar navigation, it is SSR. On client side navigation it is CSR.

When to use what

  • Do you need data at runtime but don't need SSR? Use client-side data fetching.

  • Do you need data at runtime but do need SSR? Use getServerSideProps

  • Do you have pages that rely on data that is cachable and accessible at build time? Like from a CMS? Use getStaticProps

Do you have the same requirement as above but the pages have dynamic URL params? Use getStaticProps and getStaticPaths

Rendering modes

Basically 3 rendering modes

  • Static: pages are built at run time.

  • Server Side: page are built on each request and cached after the initial hit

  • Client-side: the rendering happens on the client. The server will not send the HTML markup string. By default, the pages are pre-rendered while using Next.js.

The type of rendering is chosen based on the data fetching strategy we choose(mostly). By default, the pages are pre-rendered by Next. Pre-rendering means, the server sends down an HTML markup string to the client. Once the request is received the client will try to make it interactive by injecting listeners and handlers (hydration).

By choosing the appropriate data fetching strategy we can decide the rendering mode for the app.

If your component works with

  • DOM APIs
  • only on client data, there is no point in server-side rendering them. We can opt-out of SSR by using,
const NoSSR=dynamic(()=>import('../component'),{
 loading:()=><div>Loading.....</div>,
 ssr:false
})
Enter fullscreen mode Exit fullscreen mode

Here <NoSSR/> will always be client rendered.

Deployment

By default, it requires a Node.js environment. By using next export we can create a pure static build from our Next project and server it.

Discussion (1)

Collapse
kaustavkarmakar2 profile image
kaustav karmakar

nice article ๐Ÿฅฐ