Skip to content

Instantly share code, notes, and snippets.

@Leechael
Created March 4, 2023 16:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Leechael/1702bce29f6d8865db3b29d6b2866e3c to your computer and use it in GitHub Desktop.
Save Leechael/1702bce29f6d8865db3b29d6b2866e3c to your computer and use it in GitHub Desktop.
How to Combine Jotai Atom, Next.js Router, and Zod

As a frontend developer, I often work with React and Next.js, and one of the challenges I face is managing query parameters. Next.js provides a powerful router system, but handling query parameters can become cumbersome and error-prone. Fortunately, with the help of Jotai and zod, I've found a solution that allows me to manage query parameters in a reusable and type-safe way.

Jotai is a lightweight state management library that provides a simple and flexible way to manage state in React applications. It represents state as atoms, which are observable and subscribable values. When an atom is updated, any React components that are associated with it will automatically re-render.

Next.js is a React-based lightweight framework that provides a powerful router system, allowing you to handle client-side and server-side routing easily. Using the useRouter hook, you can access the router information, including the URL path and query parameters.

zod is a TypeScript-first data validation library that provides a simple and powerful way to validate and manipulate data. One of its essential features is the ability to convert data into the correct type, reducing type errors and making your code more robust.

To combine these three libraries to manage query parameters, I created an atomWithSchemaRouterQuery function that combines query parameters with a zod schema to provide type safety and data validation. Here's an example implementation of atomWithSchemaRouterQuery:

import { atom, useSetAtom } from 'jotai';
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { z } from 'zod';

export const routerQueryAtom = atom<Record<string, unknown>>({})

export function createHook(store: Store) {
  return useTrackRouterQuery(query: ParsedUrlQuery) {
    const router = useRouter()
    const setRouterQuery = useSetAtom(routerQueryAtom)

    if (query) {
      store.set(routerQueryAtom, query as Record<string, unknown>)
    }

    useEffect(() => {
      setRouterQuery(router.query as Record<string, unknown>)
    }, [router, setRouterQuery])
  }
}

export function atomWithSchemaRouterQuery<T extends z.Schema>(schema: T) {
  const theAtom: Atom<z.infer<typeof schema>> = atom((get) => {
    const query = get(routerQueryAtom)
    return schema.parse(query) as z.infer<typeof schema>
  })
  return theAtom
}

In this implementation, I first define a routerQueryAtom that represents the state of the query parameters. Next, I create a createHook function that subscribes to the router information and saves it in the Jotai store. Finally, in the atomWithSchemaRouterQuery function, I create a new atom using the atom function, which parses and converts the query parameters using the zod schema.

To use this function in the page, I define a zod schema for the query parameters and pass it as an argument to atomWithSchemaRouterQuery. Here's an example:

import { z } from 'zod'
import { useAtomValue } from 'jotai'
import { atomWithSchemaRouterQuery } from '../atoms/atomWithSchemaRouterQuery'

const queryParamsSchema = z.object({
  id: z.coerce.number()
})

const queryParamsAtom = atomWithSchemaRouterQuery(queryParamsSchema)

export default function BlogPostPage() {
  const { id } = useAtomValue(queryParamsAtom)
  return (
    <div>The blog post id is: {id}</div>
  )
}

In this example, I create a zod schema that defines an object with a numeric id property. I then use the atomWithSchemaRouterQuery function to create a new atom, passing in the queryParamsSchema. Finally, in the BlogPostPage component, I use the useAtomValue hook to subscribe to the queryParamsAtom atom and get the value of the id property.

Using Jotai, Next.js, and zod together to manage query parameters state can create a reusable and type-safe state management mechanism that works on both the client-side and server-side. This approach can make your code more robust and reduce the number of type errors you encounter.

In conclusion, managing query parameters in a Next.js application can be challenging, but with the help of Jotai and zod, you can create a solution that is both simple and effective. I hope this post has been helpful to you, and that you can use this knowledge to make your Next.js applications even better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment