DEV Community

Cover image for Using Query Params and Cookies in Next.js Static Pages
Aziz Abbas
Aziz Abbas

Posted on • Originally published at builder.io

Using Query Params and Cookies in Next.js Static Pages

Next.js incremental static regeneration is fantastic—it allows for delivering pre-built static pages at scale, rather than rebuilding the entire site on every deployment (cough, cough, Gatsby!). You may scale to millions of static pages while maintaining the advantages of SSG.

Static pages are built in a non-blocking way:

  • During build time
  • On revalidation hooks calls
  • On the first request after the revalidation duration is over

Because of this, developers can't out-of-the-box build pages that depend on the request context. For example, if your page design is different for logged-in users, return visitors, geographical location, etc., you'd have to render those elements on the client...but there is a solution.

Image description

Next.js Edge Middleware to the rescue

With the help of the middleware provided by Next.js, you can write functions that run halfway between the user's request initiation and completion. This way you can process a user's request and rewrite it to a static page if needed.

So in our case, we want to rewrite:

GET http://example.com/my-page
Enter fullscreen mode Exit fullscreen mode

To

GET http://example.com/my-page/{{path}}
Enter fullscreen mode Exit fullscreen mode

Where pathis a deterministic string representation of the various options your static page needs to render accurately.

So, you'd use the middleware to encode things from the request context; such as, geo location, return visitor cookies, and query params. And on the other end you'd have your static path handler decode it.

For example, you could use encodeOptions for whether or not the user is a return visitor, getting their country, and getting the page:

// middleware.js file
import { NextResponse } from 'next/server'
import { encodeOptions } from '../utils';

export default function middleware(request) {
  if (request.nextUrl.pathname === '/my-page') {
    const searchParams = request.nextUrl.searchParams
    const path = encodeOptions({
      returnVisitor: Boolean(request.cookies.get('visitor')),
      country: request.geo?.country,
      page: searchParams.get('page'),
    })

    return NextResponse.rewrite(new URL(`/my-page/${path}`, request.nextUrl))
  }
  return NextResponse.next()
}

Enter fullscreen mode Exit fullscreen mode

Then in your static path handler you can decode these options:

// /pages/my-page/[path].jsx file
import { decodeOptions } from '../../utils'

export async function getStaticProps({
  params,
}) {
  const options = decodeOptions(params.path)
  return {
    props: {
      options,
    }
  }
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: true
  }
}

export default function MyPath({ options }) {
  return <MyPage
    isReturnVisitor={options.returnVisitor}
    country={options.country} />
}
Enter fullscreen mode Exit fullscreen mode

Now we can implement our encoding/decoding functions, which could be as basic as using JSON.stringify. But we can do better than that—let's use a library that produces deterministic strings from an object:

npm install fast-json-stable-stringify
Enter fullscreen mode Exit fullscreen mode

And use it as in this snippet:

// utils.js
// https://github.com/epoberezkin/fast-json-stable-stringify
import stringify from 'fast-json-stable-stringify'

export function encodeOptions(options) {
  const json = stringify(options)
  return encodeURI(json);
}

export function decodeOptions(path) {
  return JSON.parse(decodeURI(path));
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Combining Next.js middlewares with SSG/ISR pages is super powerful and opens up a variety of use cases, such as:

  • Automatic localization of content based on geographic location, by looking at the request.geo or the Accept-Language headers.
  • Changing themes of the site based on the hosting domain, by looking at the request.headers.get('host').
  • A/B testing variations of the same page.
  • Personalizing experiences based on user type.

For more on how we use the same technique to deliver high-performing A/B testing and content personalization with Builder.io, check out A/B Testing and Personalization with Next.js Edge Middleware.

Top comments (3)

Collapse
 
fanatic007 profile image
Sarang Tayde

Nice solution, very relevant.
Can you demo ISR here?

Collapse
 
teleaziz profile image
Aziz Abbas

Thanks Sarang, passing revalidate would do it

export async function getStaticProps({
  params,
}) {
  const options = decodeOptions(params.path)
  return {
    props: {
      options,
    },
     // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every 10 seconds
    revalidate: 10, // In seconds

  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
varadan13 profile image
Aneesh

Why do they not provide the query params feature for static site generation out of the box? Was it a system constraint? Or were they thinking like query params could increase the number of pages so let's not give them that access? I know they think like we don't need access to server because SSG happens at build time but still there is a server sitting there that generates static pages for pages not included in the get static path function in this case why are they not exposing the server to us?