NextjsTips is a collection of useful Next.js tips. The goal of this website is to help you stay up-to-date with the latest features of Next.js and share handy tips which might help you.

The website is maintained by Erhan Karadeniz.

You can subscribe to the newsletter to receive a weekly recap of the tips.

Next.js has built in type support. For getStaticProps, getStaticPaths, and getServerSideProps, you can use the GetStaticProps, GetStaticPaths, and GetServerSideProps types respectively. API routes has also type support. #typescript #javascript

Copy Snippet
import { GetStaticProps, GetStaticPaths, GetServerSideProps, NextApiRequest, NextApiResponse } from 'next'

export const getStaticProps: GetStaticProps = async context => {
  // ...
}

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}

export const getServerSideProps: GetServerSideProps = async context => {
  // ...
}

export default (req: NextApiRequest, res: NextApiResponse) => {
  // ...
}

Next.js includes the next/babel preset to your app. You can extend the default config. To start, you only need to define a .babelrc file at the top of your app. This file is now the source of "truth". #javascript #reactjs #webdevelopment

Copy Snippet
// .babelrc
{
  "presets": [
    [
      "next/babel",
      {
        "preset-env": {},
        "transform-runtime": {},
        "styled-jsx": {},
        "class-properties": {}
      }
    ]
  ],
  "plugins": []
}

A zone is a single deployment of a Next.js app. You can have multiple zones and merge them as a single app. With multi zones support, you can merge multiple apps into a single one. #javascript #reactjs

Copy Snippet
// now.json
{
  "version": 2,
  "builds": [
    { "src": "blog/package.json", "use": "@now/next" },
    { "src": "home/package.json", "use": "@now/next" }
  ],
  "routes": [
    { "src": "/blog/_next(.*)", "dest": "blog/_next$1" },
    { "src": "/blog(.*)", "dest": "blog/blog$1" },
    { "src": "(.*)", "dest": "home$1" }
  ]
}

Next.js provides an integrated #TypeScript experience out of the box. To get started, create an empty tsconfig.json file in the root of your project. Then, run next and #Nextjs will guide you through the installation of the required packages to finish the setup. #reactjs #webdev

Copy Snippet
npm run dev

# You'll see instructions like these:
#
# Please install typescript, @types/react, and @types/node by running:
#
#         yarn add --dev typescript @types/react @types/node
#
# ...

Next.js allows you to configure the target browsers (for Autoprefixer and compiled #css features) through Browserslist. To customize browserslist, create a browserslist key in your package.json. #javascript #reactjs

Copy Snippet
// package.json
{
  "browserslist": [">0.3%", "not ie 11", "not dead", "not op_mini all"]
}

In order to extend our usage of webpack in #NextJS, you can define a function that extends its config inside next.config.js. The webpack function is executed twice, once for the server and once for the client. #javascript #reactjs #programming

Copy Snippet
module.exports = {
  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    // Note: we provide webpack above so you should not `require` it
    // Perform customizations to webpack config
    // Important: return the modified config
    config.plugins.push(new webpack.IgnorePlugin(/\/__tests__\//))
    return config
  },
  webpackDevMiddleware: config => {
    // Perform customizations to webpack dev middleware config
    // Important: return the modified config
    return config
  },
}

If you want to render the built-in error page you can by importing the Error component. The Error component also takes title as a property if you want to pass in a text message along with a statusCode. #javascript #webdev

Copy Snippet
import Error from 'next/error'
import fetch from 'node-fetch'

export async function getServerSideProps() {
  const res = await fetch('https://api.github.com/repos/zeit/next.js')
  const errorCode = res.ok ? false : res.statusCode
  const json = await res.json()

  return {
    props: { errorCode, stars: json.stargazers_count },
  }
}

export default function Page({ errorCode, stars }) {
  if (errorCode) {
    return <Error statusCode={errorCode} />
  }

  return <div>Next stars: {stars}</div>
}

With #Nextjs 9.3 built-in #SASS support was added. Now it’s possible to configure the compiler. For example you can now configure `includePaths`. This is possible by using the `sassOptions` key in `next.config.js`. #javascript #development #css

Copy Snippet
const path = require('path')

module.exports = {
  sassOptions: {
    includePaths: [path.join(__dirname, 'styles')]
  }
}

With #Nextjs 9.4 React Fast Refresh is introduced. Fast Refresh is a new hot reloading experience that gives you instantaneous feedback on edits made to your React components. Make sure you have no class components or unnamed default exports. #javascript #programming #reactjs

Copy Snippet
/*
* Fast Refresh is a new hot reloading experience that gives you instantaneous feedback on edits made to * your React components.
*/

// This will not get optimal results.
// pages/index.js
export default (props) => {
	return <MyComponent />
}

// This will give better results.
const Home = (props) => {
  return <MyComponent />
}
  
export default Home

#Nextjs 9.4 has better support for absolute imports and aliases. It is now possible to set a baseurl in jsconfig.js. This file also has support for paths now. #javascript #webdev #tip

Copy Snippet
// jsconfig.json or tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "."
  }
}
// Without baseUrl
import Button from '../../../../components/button'
// With baseUrl option set
import Button from 'components/button'


// tsconfig.json or jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/design-system/*": ["components/design-system/*"]
    }
  }
}
// Imports 'components/design-system/button'
import Button from '@/design-system/button'

Last week the #Google #Chrome team introduced Core Web Vitals. These are quality signals key to delivering great UX. #Nextjs 9.4 introduced `reportWebVitals`. Now you can send metrics directly to the chrome extension. #javascript #webdev

Copy Snippet
// pages/_app.js

// Will be called once for every metric that has to be reported.
export function reportWebVitals(metric) {
  // These metrics can be sent to any analytics service
  console.log(metric)
}

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

A common pain point of devs using Next.js was working with env vars. In #Nextjs 9.4 this process is more streamlined. You prefix your env variable with `NEXT_PUBLIC_` to expose it to the browser. No need for `next.config.js` to expose and .env files are loaded by default now

Copy Snippet
// pages/index.js

// The environment variable will be exposed to the browser
console.log('My Application Version', process.env.NEXT_PUBLIC_VERSION)

export default function HomePage() {
  return <h1>Hello World</h1>
}

Incremental Static Regeneration (beta) has landed with #Nextjs 9.4. This is a mechanism to update existing pages, by re-rendering them in the background as traffic comes in. Inspired by SWR, this ensures traffic is served uninterrupted, always statically. #javascript

Copy Snippet
export async function getStaticProps() {
  return {
    props: await getDataFromCMS(),
    // Next.js will attempt to re-generate the page:
    // - when a request comes in
    // - at most once every second
    unstable_revalidate: 1
  }
}

500 errors are handled both client-side and server-side by the Error component. If you wish to override it, define the file pages/_error.js and add the following code. #javascript #webdev #frontend

Copy Snippet
// pages/_error.js
function Error({ statusCode }) {
  return (
    <p>
      {statusCode
        ? `An error ${statusCode} occurred on server`
        : 'An error occurred on client'}
    </p>
  )
}

Error.getInitialProps = ({ res, err }) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404
  return { statusCode }
}

export default Error

You can create a custom 404 page for your Next.js application. To create a custom 404 page you can create a pages/404.js file. This file is statically generated at build time. #javascript #webdev

Copy Snippet
// pages/404.js
export default function Custom404() {
  return <h1>404 - Page Not Found</h1>
}

Did you know that Next.js has support for AMP pages? You can even run and hybrid AMP page. The snippet shows a page rendered as traditional HTML (default) and AMP HTML (by adding ?amp=1 to the URL) #javascript #webdevelopment

Copy Snippet
import { useAmp } from 'next/amp'

export const config = { amp: 'hybrid' }

function About(props) {
  const isAmp = useAmp()

  return (
    <div>
      <h3>My AMP About Page!</h3>
      {isAmp ? (
        <amp-img
          width="300"
          height="300"
          src="/my-img.jpg"
          alt="a cool image"
          layout="responsive"
        />
      ) : (
        <img width="300" height="300" src="/my-img.jpg" alt="a cool image" />
      )}
    </div>
  )
}

export default About

`next export` exports your app to static HTML, which can be run standalone without the need of a Node.js server. The exported app supports almost every feature of Next.js, incl. dynamic routes, prefetching, preloading and dynamic imports. #javascript

Copy Snippet
// Package.json
"scripts": {
  "build": "next build && next export"
}

/*
Afterwards from your commandline 
npm run build 

or use
next build && next export
directly from your commandline
*/

Static Generation is useful when your pages fetch data from a headless CMS. However, it’s not ideal when you’re writing a draft on your headless CMS and want to preview the draft immediately on your page. Luckily #nextjs supports this!

Copy Snippet
// pages/api/previews.js
export default async (req, res) => {
  // Check the secret and next parameters
  // This secret should only be known to this API route and the CMS
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  // Fetch the headless CMS to check if the provided `slug` exists
  // getPostBySlug would implement the required fetching logic to the headless CMS
  const post = await getPostBySlug(req.query.slug)

  // If the slug doesn't exist prevent preview mode from being enabled
  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' })
  }

  // Enable Preview Mode by setting the cookies
  res.setPreviewData({})

  // Redirect to the path from the fetched post
  // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
  res.writeHead(307, { Location: post.slug })
  res.end()
}

When doing shallowing routing, your URL will change but data fetching methods won’t be called. Keep in mind that this only works for same page URL changes! #javascript

Copy Snippet
import { useEffect } from 'react'
import { useRouter } from 'next/router'

// Current URL is '/'
function Page() {
  const router = useRouter()

  useEffect(() => {
    // Always do navigations after the first render
    router.push('/?counter=10', undefined, { shallow: true })
  }, [])

  useEffect(() => {
    // The counter changed!
  }, [router.query.counter])
}

export default Page

Next.js can serve static files, like images, under a folder called public in the root directory. Files inside public can then be referenced by your code starting from the base URL (/). This folder is also useful for robots.txt, Google Site Verification etc.

Copy Snippet
function MyImage() {
  return <img src="/my-image.png" alt="my image" />
}

export default MyImage

Component-level #CSS in in Next.js is an optional feature. You can create a file called button.module.css, import this in you button.js file. In production, all CSS Module files will be automatically concatenated into many minified and code-split .css files.

Copy Snippet
import styles from './Button.module.css'

export function Button() {
  return (
    <button
      type="button"
      // Note how the "error" class is accessed as a property on the imported
      // `styles` object.
      className={styles.error}
    >
      Destroy
    </button>
  )
}

Did you know Next.js has built-in #CSS support? It’s as simple as importing a global stylesheet in your _app.{js/tsx}. Don’t import your css in an other file, since this is not supported. You can do that with component-level CSS. #javascript #typescript

Copy Snippet
import '../styles.css'

// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

Fetching data on the client side? Use https://swr.now.sh/ by @zeithq. It handles caching, revalidation, focus tracking, refetching on interval, and more.

Copy Snippet
import useSWR from 'swr'

function Profile() {
  const { data, error } = useSWR('/api/user', fetch)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

If you export an async function called getServerSideProps from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps.

Copy Snippet
export async function getServerSideProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}

// The context parameter is an object containing the following keys:

// params: If this page uses a dynamic route, params contains the route parameters. If the page name is [id].js , then params will look like { id: ... }.

// req: The HTTP IncomingMessage object.

// res: The HTTP response object.

// query: The query string.

// preview: preview is true if the page is in the preview mode and false otherwise.

// previewData: The preview data set by setPreviewData.

Files can be read directly from the filesystem in getStaticProps with `process.cwd()`. In order to do so you have to get the full path to a file.

Copy Snippet
// This function gets called at build time on server-side.
// It won't be called on client-side.
export async function getStaticProps() {
  const postsDirectory = path.join(process.cwd(), 'posts')
  const filenames = fs.readdirSync(postsDirectory)

  const posts = filenames.map(filename => {
    const filePath = path.join(postsDirectory, filename)
    const fileContents = fs.readFileSync(filePath, 'utf8')

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

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

You can generate static pages (SSG) with dynamic routes. You'll have to use the built in functions getStaticPaths() & getStaticProps(). #javascript #JamStack

Copy Snippet
import Layout from '../../components/layout'

export default function Post() {
  return <Layout>...</Layout>
}

export async function getStaticPaths() {
  // Return a list of possible value for id
}

export async function getStaticProps({ params}) {
  // Fetch necessary data for the blog post using params.id
}