A Next.js cheat sheet repository
Project | Description |
---|---|
next-js-example-app | A bare-bone example app with local data |
- Create a new Next.js app
- ESLint
- Manual Installation
- Folder Structure
- Routing
- Meta tags
- The
_app.js
file - The
Layout
component - Sass
- Tailwind CSS
- Styled JSX
- The
_document.js
file - The
Image
component - The
Script
component - Fetch data
- Example of using
getStaticPaths
andgetStaticProps
together - Fetch Data on the client
- SWR
- When to use Static Generation v.s. Server-side Rendering
- Dynamic routes
- Custom 404 pages
- Export Static Site
- API Routes
- Check for
development
mode orproduction
mode - Custom Meta Component
- useRouter Hook
- useRouter Redirect
- Redirects
npx create-next-app
npx create-next-app --typeScript --eslint --use-npm
Add the following to the .eslintrc.json
file
{
// "extends": ["next/core-web-vitals"]
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"plugin:@next/next/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"ecmaVersion": 2020
},
"env": {
"es6": true
}
}
- Add Next.js to your project
npm install next react react-dom
- Add the following scripts to your package.json
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
Pages folder - is the only required folder in a Next.js app. All the React components inside pages folder will automatically become routes
Note: The name of the file will be the route name, use lowercase for the file name and PascalCase for the component name
Public folder - contains static assets such as images, files, etc. The files inside public folder can be accessed directly from the root of the application
Styles folder - contains stylesheets, here you can add global styles, CSS modules, etc
Usually
globals.css
is imported in the_app.js
file
Components folder - contains React components
The @
alias is used to import files from the root of the project
import Header from '@/components/Header'
To use the @
alias, add the following to the jsconfig.json
file at the root of the project
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["*"]
}
}
}
- Link - is used for client-side routing. It is similar to the HTML
<a>
tag
import Link from 'next/link'
export default function Home() {
return (
<div>
<Link href='/about'>About</Link>
</div>
)
}
- Head - is used to add meta tags to the page
import Head from 'next/head'
export default function Home() {
return (
<div>
<Head>
<title>My page title</title>
<meta name='description' content='Generated by create next app' />
<link rel='icon' href='/favicon.ico' />
</Head>
</div>
)
}
The
Head
component should be placed inside theLayout
component or inside the_app.js
file
- Import the
Head
component and put thetitle
tag inside it
Wrap around each page and here is where you would import global styles and put header and footer components
Note: You could also put the header and footer components inside the
Layout
component
- Create a
Layout
component and wrap around each page with children prop
import Header from '@/components/Header'
import Footer from '@/components/Footer'
export default function Layout({ children }) {
return (
<div>
<Header />
{children}
<Footer />
</div>
)
}
- Import the
Layout
component in the_app.js
file
import Layout from '@/components/Layout'
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default MyApp
Next.js has built-in support for Sass
- Install
sass
npm i -D sass
- Install
tailwindcss
npm install -D tailwindcss autoprefixer postcss
- Create a
tailwind.config.js
file at the root of the project
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [],
}
Note: If you are using the
src
folder, change the path to./src/pages/**/*.{js,ts,jsx,tsx}
and./src/components/**/*.{js,ts,jsx,tsx}
- Create a
postcss.config.js
file at the root of the project
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
- Add the following to
globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
- Import
globals.css
in the_app.js
file
import '@/styles/globals.css'
Styled JSX is a CSS-in-JS library that allows you to write CSS inside a React component
It has two modes: global and scoped
- Global - styles are applied globally to the entire application
export default function Home() {
return (
<>
Your JSX here
<style jsx global>{`
p {
color: red;
}
`}</style>
</>
)
}
- Scoped - styles are applied only to the component
export default function Home() {
return (
<>
Your JSX here
<style jsx>{`
p {
color: red;
}
`}</style>
</>
)
}
Note: If in vs-code the syntax highlighting for the
style
tag is not working, you can install thevscode-styled-components
extension to fix thisBe sure that the curly braces are on the same line as the
style
tag:<style jsx>{
No need to use styled jsx if you use other methods like CSS modules or styled components
Here you can customize the html
and body
tags
For instance you can add a
lang
attribute to thehtml
tag
import Document, { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang='en'>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
Note: This file will be created if you create a new Next.js app with
npx create-next-app
You can use the Image
component to add images
The images will be optimized automatically
import Image from 'next/image'
export default function Home() {
return (
<div>
<Image src='/images/profile.jpg' width={144} height={144} />
</div>
)
}
Note: src, width and height are required, alt is recommended
- if you use a remote image, you need to add the domain to the
next.config.js
file
images: {
domains: ['images.pexels.com'],
},
- or in Next.js 12.4.0:
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/account123/**',
},
],
},
You can use the Script
component to add scripts
import Script from 'next/script'
export default function Home() {
return (
<div>
<Script src='https://code.jquery.com/jquery-3.6.0.min.js' />
</div>
)
}
Note: you can add cdn scripts as well as local scripts in the
public
folder
Next.js let's you choose how to fetch data for each page. It is advised to use getStaticProps
for most of the pages and getServerSideProps
for pages with frequently updated data
- getStaticProps - is used to fetch data at build time
Note: During development with
npm run dev
,getStaticProps
runs on every request
getStaticProps
can only be exported from a page. You can't export it from non-page files
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
}
}
posts
will be passed to the component as a prop:
export default function Home({ posts }) {
return (
<div>
{posts.map((post) => (
<h3>{post.title}</h3>
))}
</div>
)
}
- getStaticPaths - is used to specify dynamic routes to pre-render pages based on data
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
const paths = posts.map((post) => ({
params: { id: post.id },
}))
return { paths, fallback: false }
}
Note: When
fallback
isfalse
, any paths not returned bygetStaticPaths
will result in a 404 pageIf
fallback
istrue
, then when a user visit a page that is not pre-rendered, Next.js will generate the page on the fly and return it to the user (useful for sites with frequently updated data like a social network)
- getServerSideProps - is used to fetch data on the server on each request
export async function getServerSideProps(context) {
return {
props: {
// props for your component
},
}
}
getStaticProps
andgetServerSideProps
have acontext
parameter that contains the urlparams
objectYou can use this to fetch data for a specific post (e.g.
context.params.id
)
Use getStaticPaths
to fetch an array of IDs and use getStaticProps
to fetch data for each product based on the ID
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
const paths = posts.map((post) => ({
params: { id: post.id },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return {
props: {
post,
},
}
}
Sometimes it can be beneficial to fetch data on the client instead of on the server.
For example, you could fetch all the static data on the server and then fetch the dynamic data on the client such as a user-specific data that changes frequently and is not needed for SEO.
- useEffect - is used to fetch data on the client
import { useEffect, useState } from 'react'
export default function Home() {
const [posts, setPosts] = useState([])
useEffect(() => {
fetch('https://.../posts')
.then((res) => res.json())
.then((data) => setPosts(data))
}, [])
return (
<div>
{posts.map((post) => (
<h3 key={post.id}>{post.title}</h3>
))}
</div>
)
}
SWR is a React Hooks library for remote data fetching on the client
You should use it instead of
useEffect
import useSWR from 'swr'
export default function Home() {
const { data, error } = useSWR('api/user', fetch)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return (
<>
{data.map((post) => (
<h3 key={post.id}>{post.title}</h3>
))}
</>
)
}
Use Static Generation whenever possible because it's much faster than Server-side Rendering and the page can be served by CDN.
You should ask yourself:
- Can I pre-render this page ahead of a user's request?
If the answer is yes, then you should choose Static Generation.
- Does the page need to update frequently?
If the answer is yes, then you should choose Server-side Rendering.
You can use Static Generation for many types of pages, including:
- Marketing pages
- Blog posts
- E-commerce product listings
- Help and documentation
You could also skip Server-side Rendering and use client-side JavaScript to fetch data with useEffect
-
Create a folder inside the
pages
folder with the name of the dynamic route in square brackets (e.g.[id]
) -
Create an
index.js
file inside the dynamic route folder
- Create a link with that points to the dynamic route and pass the dynamic value as a prop
import Link from 'next/link'
export default function Post({ post }) {
return (
<div>
<Link href='/posts/[id]' as={`/posts/${post.id}`}>
<a>{post.title}</a>
</Link>
</div>
)
}
Note: this is usually done inside a
map
function
Dynamic routes can be extended to catch all paths by adding three dots (...) inside the brackets. For example:
pages/posts/[...id].js
matches/posts/a
, but also/posts/a/b
,/posts/a/b/c
and so on.
If you do this, in getStaticPaths, you must return an array as the value of the id key like so:
return [
{
params: {
// Statically Generates /posts/a/b/c
id: ['a', 'b', 'c'],
},
},
//...
]
And params.id will be an array in getStaticProps:
export async function getStaticProps({ params }) {
// params.id will be like ['a', 'b', 'c']
}
- Create a
404.js
file inside thepages
folder
export default function Custom404() {
return <h1>404 - Page Not Found</h1>
}
Note: You can also create a
500.js
file for the server error page
Export a static site with next export
Add an npm script to the
package.json
file:
"scripts": {
"export": "next build && next export"
}
Run the script:
npm run export
The static site will be exported to the
out
folderYou can deploy this folder to any static site host such as GitHub Pages
- Install
serve
npm i -g serve
- Run the server
serve -s out -p 8000
You can work with any database in the pages/api/
folder
Note: Any API route that is placed inside this folder will be accessible like any other page in Next.js
- Create a folder inside the
pages
folder with the name of the API route (e.g.api/posts
)
- Create a
data.js
file at the root of the project
const posts = [
{
id: 1,
title: 'Post 1',
},
{
id: 2,
title: 'Post 2',
},
{
id: 3,
title: 'Post 3',
},
]
- Import the data in the API route
import { posts } from '@/data'
- Get the data
export default function handler(req, res) {
res.status(200).json(posts)
}
You can now fetch the data as you would with any other API
Note: Next.js needs absolute paths when fetching data
Since Next.js needs absolute paths when fetching data, you can check if you are in development
mode or production
mode
- Create a
config.js
folder at the root of the project with anindex.js
file inside
const dev = process.env.NODE_ENV !== 'production'
export const server = dev ? 'http://localhost:3000' : 'https://yourwebsite.com'
- Now you can use
server
as a variable in your code as an absolute path when fetching data
import { server } from '@/config'
export default function handler(req, res) {
fetch(`${server}/api/posts`)
.then((res) => res.json())
.then((data) => res.status(200).json(data))
}
Note: There is no need to create a custom meta component since we can use the
Head
component from Next.js
A meta component is used to add meta tags to the head
of the document
- Create a
Meta.js
file inside thecomponents
folder
import Head from 'next/head'
export default function Meta({ title, keywords, description }) {
return (
<Head>
<meta charSet='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel='icon' href='/favicon.ico' />
<meta name='keywords' content={keywords} />
<meta name='description' content={description} />
<title>{title}</title>
</Head>
)
}
Tip: You can also use packages such as
next-seo
for this
- Add
defaultProps
to theMeta
component so that you don't need to add props to it every time you use it
Meta.defaultProps = {
title: 'WebDev News',
keywords: 'web development, programming',
description: 'Get the latest news in web dev',
}
- Now you can use the
Meta
component in any page (it is common to use it in theLayout
component)
import Meta from '@/components/Meta'
export default function Layout({ children }) {
return (
<div>
<Meta />
{children}
</div>
)
}
- Import the
Meta
component in the specific page and pass the title as a prop
import Meta from '@/components/Meta'
export default function About() {
return (
<div>
<Meta title='About' />
<h1>Welcome to the About Page</h1>
</div>
)
}
useRouter is a hook that gives you access to the router object
- Import the
useRouter
hook
import { useRouter } from 'next/router'
- Use the
useRouter
hook
const router = useRouter()
- Get the query
const router = useRouter()
const { query } = router
- Get the query with destructuring
const {
query: { id },
} = useRouter()
useRouter main properties:
pathname
- Current route. That is the path of the page inpages
route
- Current route with the query stringquery
- Query string section of URL parsed as an objectasPath
- String of the actual path (including the query) shown in the browser
- Import the
useRouter
hook
import { useRouter } from 'next/router'
- Use the
useRouter
hook to redirect the user to home page
const router = useRouter()
router.push('/')
Note: You can for instance use this hook in a
404
page to redirect the user to the home page after 3 seconds
To redirect a user to another page, you can use redirects
on next.config.js
module.exports = {
async redirects() {
return [
{
source: '/about',
destination: '/',
permanent: false,
},
]
},
}
Note:
permanent: true
will tell the browser to cache the redirect forever. That means that if the user goes to the/about
page, the browser will redirect the user to the/
page without making a request to the serverTIP: Do not use
permanent: true
for redirects that are not permanent
308
- Permanent Redirect307
- Temporary Redirect
Note:
308
replaces301
,307
replaces302