Skip to content

Params and Search Params

Another use case for Tempeh is to have typesafe parameters for your routes. This is useful when you want to pass parameters to your routes and you want to make sure that the parameters are of the correct type.

Tempeh extends the Route configuration to provide type to the native useSearchParams and useParams hooks provided by next/router.

Let's see this in action

Example

Suppose you have a workspaces page in Next.js and it may take multiple parameters like projectId. You can define the types for these parameters in the route configuration.

Similarly it may also take a query parameter like sortBy, limit, query etc to sort workspace activities or filter them.

---
SearchParams:
  projectId: number
  sortBy: string
  limit: number
  query: string
 
Params:
  projectId: string
---

File Structure

├── root
|  |
|  ├── app
|  |  ├── layout.tsx
|  |  ├── page.tsx
|  |  |
|  |  ├── workspaces
|  |  |  ├── [projectId]
|  |  |  |  ├── page.tsx
|  |  |  |  ├── route.info.ts
|  |
|  |
|  ├── package.json
|  ├── next.config.mjs

As you can see we are just extending the route configuration to each page level here so that we have a single source of truth for all the parameters and query parameters attributed to a single route. this way managing the routes and parameters becomes easy.

workspaces/route.info.ts

app/workspaces/page.tsx
import * as z from "zod";
import { routeBuilder } from "tempeh";
 
// Ideally this must be in the route.config.ts, but for the sake of example, we have shown it here
 
const { createRoute } = routeBuilder({
  additionalBaseUrls: {
    APP: "https://app.example.com",
    API: "https://api.example.com",
  },
  defaultBaseUrl: "/",
}).getInstance();
 
// we will create our route config here
 
const paramSchema = z.object({
  projectId: z.string(),
});
 
const searchParamSchema = z.object({
  sortBy: z.enum(["asc", "desc"]).optional().default("asc"),
  limit: z.string().pipe(z.coerce.number()).optional(),
  query: z.string().optional(),
});
 
const WorkspaceRoute = createRoute({
  name: "WorkspaceRoute",
  fn: ({ projectId }) => `/workspaces/${projectId}`,
  paramsSchema: paramSchema,
  searchParamsSchema: searchParamSchema,
});
 
export default WorkspaceRoute;

Usage

Now you can use the WorkspaceRoute in your components to link to the workspace page. From any page in the application you can link to the workspace page by passing the projectId and other search params. This way you will never have to worry about the type of the parameters and query parameters.

Suppose inside a filter component you want to access to the workspace link with the query parameters sortBy, limit and query.

Note: It only works in the client components as it uses the next/router hooks internally.

app/workspaces/[projectId]/components/filter.tsx
"use client";
import WorkspaceRoute from "../../route.info";
 
export const Filter = () => {
  const { projectId } = WorkspaceRoute.useParams();
  const { sortBy, limit, query } = WorkspaceRoute.useSearchParams();
 
  return (
    <div>
      {/** using the values of search params here*/}
      <Activities sort={sortBy} limit={limit} query={query} />
    </div>
  );
};
 
export default Filter;

Note: useParams and useSearchParams throw if the params and searchParams extracted by the hooks do not pass the validation check imposed by the schema.

Safe Usage

Sometimes we do care about errors thrown by the useParams and useSearchParams. In such situations, useParams and useSearchParams take a boolean flag as option - safe. When turned on, the hooks return an object with two properties - value and error. The value property contains the extracted params and searchParams, and the error property contains the error thrown by the schema validation. Both of these values will be seperated out by the tag success making it easy to pass in pattern matching or conditional logic.

success -> true :: value will be returned
success -> false :: error will be returned

Things to note:

  • The fact that we are using zod here, we can always define in our schema to transform the values to the desired type. In the above example we are coercing the limit to a number.
  • This is only available in the client side components as it uses the next/router hooks internally.
  • The tempeh also provides a params and searchParams property from the built routeConfig but there are caviats to it:
    • It is solely made to infer type so you can't access the values directly from it.

This is how you can use the params and search params to infer types in your Next.js server components with Tempeh.

parseParams & parseSearchParams

Apart from hooks, createRoute also exposes two functions parseParams and parseSearchParams which can be used to parse the params and searchParams from the URL. These functions are useful when you want to parse the params and searchParams in a function or a utility file. A great use case of this is to parse params returned by Next.js Page

import WorkspaceRoute from "../../route.info";
 
const WorkspacePage = ({ params }: { params: unknown }) => {
  // Will throw error
  const { workspaceId } = WorkspaceRoute.parseParams(params);
  // Will not throw error
  const safeParams = WorkspaceRoute.parseParams(params, { safe: true });
 
  // use returned value pass in the props or render conditionally
 
 
  return (
    <div>
      {/** using the values of search params here*/}
      <Activities projectId = {projectId} />
    </div>
  );
};