Categories
Next.js

Next.js — Route Links and Dynamic Routes

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at routing with Next.js.

Dynamic Route Links

We can add links for dynamic routes.

To do this, we write:

pages/foo.js

import Links from './links';

function Foo() {
  return <div>
    <Links />
    <p>foo</p>
  </div>
}

export default Foo

pages/blog/[slug].js

import { useRouter } from 'next/router'
import Links from '../links';

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

  return <div>
    <Links />
    <p>Post: {slug}</p>
  </div>
}

export default Post

pages/links.js

import Link from 'next/link'

const posts = [
  { slug: 'hello-world', title: 'hello world', id: 1 },
  { slug: 'good-news', title: 'good news', id: 2 },
  { slug: 'great-news', title: 'great news', id: 3 },
]

function Links() {
  return (
    <ul>
      <li>
        <Link href="/foo">
          <a>foo</a>
        </Link>
      </li>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href="/blog/[slug]" as={`/blog/${post.slug}`}>
            <a>{post.title}</a>
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Links

We have the [slug].js file which displays the slug that we pass in.

router.query has the URL parameters in an object.

It can have as many URL parameters as we pass in.

The useRouter hook has the route data.

We need the brackets to indicate that the file is used for dynamic routes that can accept URL parameters.

The links.js file has the links.

We use the Link component for adding the links.

The href has the URL pattern for the route.

And as has the actual path for the route to fo to.

Also, we have a static link to the foo page.

Therefore, when we click the links, we’ll go to the pages that are mapped to the routes.

We should see the slug when we click on the last 3 links.

Catch All Routes

We can add catch-all routes by adding ... before the file name.

For example, we can write:

pages/blog/[...slug].js:

import { useRouter } from 'next/router'
import Links from '../links';

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

  return <div>
    <Links />
    <p>Post: {slug.join(', ')}</p>
  </div>
}

export default Post

pages/links.js

import Link from 'next/link'

const posts = [
  { slug: 'hello-world/1', title: 'hello world', id: 1 },
  { slug: 'good-news/2', title: 'good news', id: 2 },
  { slug: 'great-news/3', title: 'great news', id: 3 },
]

function Links() {
  return (
    <ul>
      <li>
        <Link href="/foo">
          <a>foo</a>
        </Link>
      </li>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href="/blog/[slug]" as={`/blog/${post.slug}`}>
            <a>{post.title}</a>
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Links

We added an extra URL parameter to each slug .

They’re rendered as arrays in [...slug].js .

Therefore, we called join to convert the array into a comma-separated string.

Optional Catch-all Routes

We can make catch-all routes optional by adding extra brackets around the file name.

For example, we can write:

pages/blog/[[...slug]].js

import { useRouter } from 'next/router'
import Links from '../links';

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

  return <div>
    <Links />
    <p>Post: {slug && slug.join(', ')}</p>
  </div>
}

export default Post

We wrapped an extra set of brackets around the file name.

Now we can go to the /blog path and we’ll see the same page without anything after Post: .

Conclusion

We can add dynamic route links with Next.js.

All we have to do is to follow the file name convention and use the href and as props with the Link component.

Categories
Next.js

Next.js — Rendering Data

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to render data with Next.js

Static Rendering

We can create pages with dynamic routes.

To do that, we add a getStaticPaths function to our component file and export it.

Also, we’ve to add brackets around the name of the file to make it dynamic.

For example, we create a country/[name].js file to add a dynamic route to get the country data and display it with Next.js.

In country/[name].js , we write:

function Country({ country }) {
  return <p>{country.name}</p>
}

export async function getStaticPaths() {
  const res = await fetch('https://restcountries.eu/rest/v2/all')
  const countries = await res.json()
  const paths = countries.map((country) => `/country/${country.name.toLowerCase()}`)
  return { paths, fallback: false }
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://restcountries.eu/rest/v2/name/${params.name}`)
  const [country] = await res.json()
  return { props: { country } }
}

export default Country

We have the getStaticPaths method to return all the paths that we want to prerender.

All we have to do is get the data from the REST countries API and create an array of paths that we want to render.

fallback: false means that other routes that aren’t rendered should return 404.

Also, we have the getStaticProps function to get the data for the individual pre-rendered routes.

The params object has the URL parameters that we passed in.

In the Country component, we render the result that comes from getStaticProps .

Now when we go to a URL like http://localhost:3000/country/canada, we should see the country name displayed.

This will pre-render the pages statically and use them for all requests.

Static generation is good for pages that don’t change much like marketing pages, blog posts, product listings, and documentation.

Server-side Rendering

An alternative way to display render content with Next.js is with server-side rendering.

To do that, we add the getServerSideProps function to our page.

For example, we can create a pages/yesno.js file and write:

function YesNo({ data }) {
  return <p>{data.answer}</p>
}

export async function getServerSideProps() {
  const res = await fetch(`https://yesno.wtf/api`)
  const data = await res.json()
  return { props: { data } }
}

export default YesNo

We have the getServerSideProps function to get the data.

And then we get the data that’s fetched in the props of the YesNo component and render it.

Conclusion

We can render data in 2 different ways with Next.js.

One way is to pre-render routes statically. It’s faster and it’s recommended

The other way is to render routes with server-side rendering.

Categories
Next.js

Next.js — Environment Variables and Routing

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to expose environment variables to the browser and routing.

Exposing Environment Variables to the Browser

We can expose our environment variable to the browser if we prefix our variable with the NEXT_PUBLIC_ prefix.

For example, we can write:

NEXT_PUBLIC_API_KEY=...

Then we can use it by writing:

function Photos({ data }) {
  return <p>{JSON.stringify(data)}</p>
}

export async function getServerSideProps() {
  const headers = new Headers();
  headers.append('Authorization', process.env.NEXT_PUBLIC_API_KEY);
  const res = await fetch(`https://api.pexels.com/v1/search?query=people`, {
    method: 'GET',
    headers,
    mode: 'cors',
    cache: 'default',
  })
  const data = await res.json()
  return { props: { data } }
}

export default Photos

We access the environment variable from the process.env property.

Default Environment Variables

We can add default environment variables in the .env , .env.development , and .env.production files.

The .env.local file will override the values from those files.

And the .env*.local files should be added to .gitignore since this is where we should store secrets.

Environment Variables on Vercel

We can also store environment variables on Vercel’s environment variables section.

We still use .env , .env.development and .env.production to store default values.

The values can be pulled into .env.local by running:

vercel env pull .env.local

Test Environment Variables

Also, we can use the test environment to store environment variables for any test environment.

This is useful for setting up environments for running automated tests.

The test values will be used if the NODE_ENV is set to test .

.env.local won’t be loaded when the app is running in the test environment.

Supported Browsers and Features

Next.js supports IE11 and all modern browsers like Chrome, Firefox, and Edge.

Next.js injects polyfills for IE11 compatibility.

Therefore, we don’t have to worry about missing features on IE11.

Server-side polyfills are also injected.

This way, we can use fetch in the server-side environment without adding our own polyfills.

Next.js makes sure that modern JavaScript features like:

  • Async/await (ES2017)
  • Object Rest/Spread Properties (ES2018)
  • Dynamic import() (ES2020)
  • Optional Chaining (ES2020)
  • Nullish Coalescing (ES2020)
  • Class Fields and Static Properties

all work out of the box.

Routing

Routing is done automatically with Next.js

The folder structure of the pages folder determines the routes that are defined.

For example, if we have page/index.js is mapped to / .

And pages/posts/index.js is mapped to /posts .

We can just nested our files and they’ll be mapped to URLs with the path of the files.

Dynamic Route Segments

We surround our filename with brackets to make them dynamic.

For example, if we have pages/blog/[slug].js , then it’s mapped to the /blog/:slug URL.

If we have pages/post/[…all].js , then we can have any number of URL parameters after /post .

For example, it’ll match /post/2020/id/title .

Linking Between Pages

We can add links between pages with the Link component.

For example, we can write:

pages/links.js

import Link from 'next/link'

function Links() {
  return (
    <ul>
      <li>
        <Link href="/foo">
          <a>foo</a>
        </Link>
      </li>
      <li>
        <Link href="/bar">
          <a>bar</a>
        </Link>
      </li>
    </ul>
  )
}

export default Links

pages/foo.js

import Links from './links';

function Foo() {
  return <div>
    <Links />
    <p>foo</p>
  </div>
}

export default Foo

pages/bar.js

import Links from './links';

function Bar() {
  return <div>
    <Links />
    <p>bar</p>
  </div>
}
export default Bar

to add the Links component with the Link components to add the links to the pages.

And we use Links in the Foo and Bar components to add the routes.

Now we can click on them to jump between routes.

Conclusion

We can use environment variables in the browser environment with Next.js.

Also, we can add links to our pages with the Link component.

Routing is done by following some conventions.

Categories
Next.js

Next.js — CSS-in-JS, Fast Refresh, and Environment Variables

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at how to style pages, use fast refresh, and add environment variables with Next.js.

Less and Stylus Support

Next.js support Less and Stylus for styling.

To add support for it, we install the @zeit/next-less and @zeit/next-stylus plugins.

CSS-in-JS

We can also use any CSS-in-JS solution for styling our Next.js app.

For example, we can add inline styles:

function Hello() {
  return <p style={{ color: 'blue' }}>hello world</p>
}

export default Hello

We can also use styled-jsx to put CSS in our JavaScript files.

It comes with Next.js.

For example, we can write:

function Hello() {
  return <div>
    <style jsx>{`
        p {
          color: blue;
        }
        div {
          background: red;
        }
        @media (max-width: 600px) {
          div {
            background: blue;
          }
        }
      `}</style>
    <style global jsx>{`
        body {
          background: lightgray;
        }
      `}</style>
    <p>hello world</p>
  </div>
}

export default Hello

We add style tags which contains local and global styles.

Global styles have the global prop and local ones don’t.

Fast Refresh

Fast refresh is a Next.js feature that makes development easier.

It gives us instant feedback on edits made to our React components.

It’s enabled by default on Next 9.4 or later.

This feature lets make edits visible without losing component state.

It’ll reload the component if we edit a component.

If we edit something that that’s not a component but it’s used by them, then both files will be refreshed.

If a file that’s imported by files outside of the refresh tree, then fast refresh will do a full reload.

When any runtime errors are encountered, then we’ll see an overlay with the error displayed.

If we added error boundaries to our app, then they’ll retry rendering on the next edit.

If there’re any hooks, then their state will be updated when a fast refresh happens.

Static File Serving

Next.js apps can have static files.

We just have to put them inside the public folder.

For example, we can write:

function Hello() {
  return <div>
    <img src="/kitten.jpg" alt="kitten" />
  </div>
}

export default Hello

given that kitten.jpg is in the public folder.

We’ll see the kitten picture displayed.

We shouldn’t change the name of the public folder since it’s the only folder that can be used to serve static assets.

Environment Variables

We can load environment variables from an .env.local file.

For example, we can create an .env.local file by writing:

API_KEY=...

And then we can create a page that uses the API_KEY environment variable with:

function Photos({ data }) {
  return <p>{JSON.stringify(data)}</p>
}

export async function getServerSideProps() {
  const headers = new Headers();
  headers.append('Authorization', process.env.API_KEY);
  const res = await fetch(`https://api.pexels.com/v1/search?query=people`, {
    method: 'GET',
    headers,
    mode: 'cors',
    cache: 'default',
  })
  const data = await res.json()
  return { props: { data } }
}

export default Photos

We access our environment variable with the process.env object.

These environment variables are only available in the Node.js environment.

Conclusion

Next.js 9.4 or later comes with the fast refresh feature to make development more convenient.

Also, Next.js comes with CSS-in-JS support.

We can also use environment variables in our apps.

Categories
Next.js

Next.js — Client-Side Navigation and API Routes

We can create server-side rendered React apps and static sites easily Next.js.

In this article, we’ll take a look at routing with Next.js.

Client-Side Navigation

In addition to routing on server-side, we can also do client-side navigation with Next.js.

For example, we can write:

pages/links.js

import { useRouter } from 'next/router'

function Links() {
  const router = useRouter()

  return (
    <ul>
      <li>
        <span onClick={() => router.push('/foo')}>foo</span>
      </li>
      <li>
        <span onClick={() => router.push('/bar')}>bar</span>
      </li>
    </ul>
  )
}

export default Links

pages/foo.js

import Links from './links';

function Foo() {
  return <div>
    <Links />
    <p>foo</p>
  </div>
}

export default Foo

pages/bar.js

import Links from './links';

function Bar() {
  return <div>
    <Links />
    <p>bar</p>
  </div>
}
export default Bar

We have the pages/links.js file with spans that runs the router.push method on click.

The router object comes from Next.js’s useRouter hook.

Shallow Routing

Shallow routing lets us change the URL without running data fetching methods like getServerSideProps , getStaticProps , and getInitialProps again.

We get the updated pathname and query via the router object.

For example, we can write:

pages/count.js

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

function Count() {
  const router = useRouter()

  useEffect(() => {
    router.push('/count?count=10', undefined, { shallow: true })
  }, [])

  useEffect(() => {
  }, [router.query.count])

  return <p>{router.query.count}</p>
}

export default Count

to add a Count component with a useEffect hook that calls router.push to add a query string to the URL.

shallow: true means that we enable shallow routing.

We should see 10 displayed since router.query.count is 10 as we see from the query string when we go to http://localhost:3000/count.

Shallow routing only works for same page URL changes.

So if we have:

router.push('/?count=10', '/about?count=10', { shallow: true })

then the about page would be loaded from scratch.

API Routes

We can use API routes to build an API with Next.js.

To do that , we can put JavaScrtipt files inside the pages/api folder.

Then the files inside would be mapped to the /api/* URLs.

For example, we can write:

pages/api/hello.js

export default (req, res) => {
  res.statusCode = 200
  res.json({ name: 'hello world' })
}

req has the requests data and res is an object we can use to create our response.

We set the response status code with res.statusCode and we create our JSON response with the res.json method.

Now when we make a request to http://localhost:3000/api/hello with any HTTP verb, we’ll see:

{
  "name": "hello world"
}

returned.

To handle different HTTP methods, in an API route, we can use the req.method property.

For example, we can write:

export default (req, res) => {
  res.statusCode = 200
  res.json({ name: 'hello world', method: req.method })
}

Now when we make a PUT request to http://localhost:3000/api/hello, we get:

{
    "name": "hello world",
    "method": "PUT"
}

returned.

API routes don’t specify CORS headers, so we can only make these requests if the request originates from the same origin as the API route.

Conclusion

We can make API routes with Next.js.

Navigation can be done from the client side with the useRouter hook.