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.

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
}