Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

next-safe-navigation

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

next-safe-navigation

Type-safe navigation for NextJS App router

  • 0.3.2
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
897
decreased by-18.08%
Maintainers
1
Weekly downloads
 
Created
Source

Goggles emoji

Safe NextJS Navigation

Latest build Latest published version Bundlephobia Tree shaking available Types included License Number of downloads GitHub Stars

Static type and runtime validation for navigating routes in NextJS App Router with Zod schemas.

Static and runtime validation of routes, route params and query string parameters on client and server components.

📦 Install

Safe NextJS Navigation is available as a package on NPM, install with your favorite package manager:

npm install next-safe-navigation

⚡ Quick start

[!TIP] Enable experimental.typedRoutes in next.config.js for a better and safer experience with autocomplete when defining your routes

Declare your application routes and parameters in a single place

// src/shared/navigation.ts
import { createNavigationConfig } from "next-safe-navigation";
import { z } from "zod";

export const { routes, useSafeParams, useSafeSearchParams } = createNavigationConfig(
  (defineRoute) => ({
    home: defineRoute('/'),
    customers: defineRoute('/customers', {
      search: z
        .object({
          query: z.string().default(''),
          page: z.coerce.number().default(1),
        })
        .default({ query: '', page: 1 }),
    }),
    invoice: defineRoute('/invoices/[invoiceId]', {
      params: z.object({
        invoiceId: z.string(),
      }),
    }),
    shop: defineRoute('/support/[...tickets]', {
      params: z.object({
        tickets: z.array(z.string()),
      }),
    }),
    shop: defineRoute('/shop/[[...slug]]', {
      params: z.object({
        // ⚠️ Remember to always set your optional catch-all segments
        // as optional values, or add a default value to them
        slug: z.array(z.string()).optional(),
      }),
    }),
  }),
);

Runtime validation for React Server Components (RSC)

[!IMPORTANT] The output of a Zod schema might not be the same as its input, since schemas can transform the values during parsing (e.g.: z.coerce.number()), especially when dealing with URLSearchParams where all values are strings and you might want to convert params to different types. For this reason, this package does not expose types to infer params or searchParams from your declared routes to be used in page props:

interface CustomersPageProps {
  // ❌ Do not declare your params | searchParam types
  searchParams?: ReturnType<typeof routes.customers.$parseSearchParams>
}

Instead, it is strongly advised that you parse the params in your server components to have runtime validated and accurate type information for the values in your app.

// src/app/customers/page.tsx
import { routes } from "@/shared/navigation";

interface CustomersPageProps {
  // ✅ Never assume the types of your params before validation
  searchParams?: unknown
}

export default async function CustomersPage({ searchParams }: CustomersPageProps) {
  const { query, page } = routes.customers.$parseSearchParams(searchParams);

  const customers = await fetchCustomers({ query, page });

  return (
    <main>
      <input name="query" type="search" defaultValue={query} />

      <Customers data={customers} />
    </main>
  )
};

/* --------------------------------- */

// src/app/invoices/[invoiceId]/page.tsx
import { routes } from "@/shared/navigation";

interface InvoicePageProps {
  // ✅ Never assume the types of your params before validation
  params?: unknown
}

export default async function InvoicePage({ params }: InvoicePageProps) {
  const { invoiceId } = routes.invoice.$parseParams(params);

  const invoice = await fetchInvoice(invoiceId);

  return (
    <main>
      <Invoice data={customers} />
    </main>
  )
};

Runtime validation for Client Components

// src/app/customers/page.tsx
'use client';

import { useSafeSearchParams } from "@/shared/navigation";

export default function CustomersPage() {
  const { query, page } = useSafeSearchParams('customers');

  const customers = useSuspenseQuery({
    queryKey: ['customers', { query, page }],
    queryFn: () => fetchCustomers({ query, page}),
  });

  return (
    <main>
      <input name="query" type="search" defaultValue={query} />

      <Customers data={customers.data} />
    </main>
  )
};

/* --------------------------------- */

// src/app/invoices/[invoiceId]/page.tsx
'use client';

import { useSafeParams } from "@/shared/navigation";

export default function InvoicePage() {
  const { invoiceId } = useSafeParams('invoice');

  const invoice = useSuspenseQuery({
    queryKey: ['invoices', { invoiceId }],
    queryFn: () => fetchInvoice(invoiceId),
  });

  return (
    <main>
      <Invoice data={invoice.data} />
    </main>
  )
};

Use throughout your codebase as the single source for navigating between routes:

import { routes } from "@/shared/navigation";

export function Header() {
  return (
    <nav>
      <Link href={routes.home()}>Home</Link>
      <Link href={routes.customers()}>Customers</Link>
    </nav>
  )
};

export function CustomerInvoices({ invoices }) {
  return (
    <ul>
      {invoices.map(invoice => (
        <li key={invoice.id}>
          <Link href={routes.invoice({ invoiceId: invoice.id })}>
            View invoice
          </Link>
        </li>
      ))}
    </ul>
  )
};

Keywords

FAQs

Package last updated on 22 Apr 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc