using-nuqs

Manage React state in URL query parameters with nuqs. Covers Suspense boundaries, parsers, clearing state, and deep-linkable dialogs.

9 stars

Best use case

using-nuqs is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Manage React state in URL query parameters with nuqs. Covers Suspense boundaries, parsers, clearing state, and deep-linkable dialogs.

Teams using using-nuqs should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/using-nuqs/SKILL.md --create-dirs "https://raw.githubusercontent.com/andrelandgraf/fullstackrecipes/main/.agents/skills/using-nuqs/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/using-nuqs/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How using-nuqs Compares

Feature / Agentusing-nuqsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Manage React state in URL query parameters with nuqs. Covers Suspense boundaries, parsers, clearing state, and deep-linkable dialogs.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Working with nuqs

Manage React state in URL query parameters with nuqs. Covers Suspense boundaries, parsers, clearing state, and deep-linkable dialogs.

## Implement Working with nuqs

Manage React state in URL query parameters with nuqs for shareable filters, search, and deep-linkable dialogs.

**See:**

- Resource: `using-nuqs` in Fullstack Recipes
- URL: https://fullstackrecipes.com/recipes/using-nuqs

---

### Suspense Boundary Pattern

nuqs uses `useSearchParams` behind the scenes, requiring a Suspense boundary. Wrap nuqs-using components with Suspense via a wrapper component to keep the boundary colocated:

```typescript
import { Suspense } from "react";

type SearchInputProps = {
  placeholder?: string;
};

// Public component with built-in Suspense
export function SearchInput(props: SearchInputProps) {
  return (
    <Suspense fallback={<input placeholder={props.placeholder} disabled />}>
      <SearchInputClient {...props} />
    </Suspense>
  );
}
```

```typescript
"use client";

import { useQueryState, parseAsString } from "nuqs";

// Internal client component that uses nuqs
function SearchInputClient({ placeholder = "Search..." }: SearchInputProps) {
  const [search, setSearch] = useQueryState("q", parseAsString.withDefault(""));

  return (
    <input
      value={search}
      onChange={(e) => setSearch(e.target.value || null)}
      placeholder={placeholder}
    />
  );
}
```

This pattern allows consuming components to use `SearchInput` without adding Suspense themselves.

### State to URL Query Params

Replace `useState` with `useQueryState` to sync state to the URL:

```typescript
"use client";

import {
  useQueryState,
  parseAsString,
  parseAsBoolean,
  parseAsArrayOf,
} from "nuqs";

// String state (search, filters)
const [search, setSearch] = useQueryState("q", parseAsString.withDefault(""));

// Boolean state (toggles)
const [showArchived, setShowArchived] = useQueryState(
  "archived",
  parseAsBoolean.withDefault(false),
);

// Array state (multi-select)
const [tags, setTags] = useQueryState(
  "tags",
  parseAsArrayOf(parseAsString).withDefault([]),
);
```

### Clear State

Set to `null` to remove from URL:

```typescript
// Clear single param
setSearch(null);

// Clear all filters
function clearFilters() {
  setSearch(null);
  setTags(null);
  setShowArchived(null);
}
```

When using `.withDefault()`, setting to `null` clears the URL param but returns the default value.

### Deep-Linkable Dialogs

Control dialog visibility with URL params for shareable links:

```typescript
import { Suspense } from "react";

type DeleteDialogProps = {
  onDelete: (id: string) => Promise<void>;
};

// Public component with built-in Suspense
export function DeleteDialog(props: DeleteDialogProps) {
  return (
    <Suspense fallback={null}>
      <DeleteDialogClient {...props} />
    </Suspense>
  );
}
```

```typescript
"use client";

import { useQueryState, parseAsString } from "nuqs";
import { AlertDialog, AlertDialogContent } from "@/components/ui/alert-dialog";

function DeleteDialogClient({ onDelete }: DeleteDialogProps) {
  const [deleteId, setDeleteId] = useQueryState("delete", parseAsString);

  async function handleDelete() {
    if (!deleteId) return;
    await onDelete(deleteId);
    setDeleteId(null);
  }

  return (
    <AlertDialog open={!!deleteId} onOpenChange={(open) => !open && setDeleteId(null)}>
      <AlertDialogContent>
        {/* Confirmation UI */}
        <Button onClick={handleDelete}>Delete</Button>
      </AlertDialogContent>
    </AlertDialog>
  );
}
```

Open the dialog programmatically:

```typescript
// Open delete dialog for specific item
setDeleteId("item-123");

// Deep link: /items?delete=item-123
```

### Opening Dialogs from Buttons

Use a trigger button to open the dialog:

```typescript
function ItemRow({ item }: { item: Item }) {
  const [, setDeleteId] = useQueryState("delete", parseAsString);

  return (
    <Button variant="ghost" onClick={() => setDeleteId(item.id)}>
      Delete
    </Button>
  );
}
```

---

## References

- [nuqs Documentation](https://nuqs.47ng.com/)
- [nuqs Parsers](https://nuqs.47ng.com/docs/parsers)

Related Skills

using-workflows

9
from andrelandgraf/fullstackrecipes

Create and run durable workflows with steps, streaming, and agent execution. Covers starting, resuming, and persisting workflow results.

using-user-stories

9
from andrelandgraf/fullstackrecipes

Document and track feature implementation with user stories. Workflow for authoring stories, building features, and marking acceptance criteria as passing.

using-tests

9
from andrelandgraf/fullstackrecipes

Testing strategy and workflow. Tests run in parallel with isolated data per suite. Prioritize Playwright for UI, integration tests for APIs, unit tests for logic.

using-sentry

9
from andrelandgraf/fullstackrecipes

Capture exceptions, add context, create performance spans, and use structured logging with Sentry.

using-logging

9
from andrelandgraf/fullstackrecipes

Use structured logging with Pino throughout your application. Covers log levels, context, and workflow-safe logging patterns.

using-drizzle-queries

9
from andrelandgraf/fullstackrecipes

Write type-safe database queries with Drizzle ORM. Covers select, insert, update, delete, relational queries, and adding new tables.

using-authentication

9
from andrelandgraf/fullstackrecipes

Use Better Auth for client and server-side authentication. Covers session access, protected routes, sign in/out, and fetching user data.

using-analytics

9
from andrelandgraf/fullstackrecipes

Track custom events and conversions with Vercel Web Analytics. Covers common events, form tracking, and development testing.

nuqs-setup

9
from andrelandgraf/fullstackrecipes

Sync React state to URL query parameters for shareable filters, search queries, and deep links to modal dialogs. Preserves UI state on browser back/forward navigation.

url-state-management

9
from andrelandgraf/fullstackrecipes

Sync React state to URL query parameters for shareable filters, search, and deep-linkable dialogs with nuqs.

testing

9
from andrelandgraf/fullstackrecipes

Complete testing setup with Neon database branching, Playwright browser tests, integration tests, and unit tests. Isolated branches with automatic TTL cleanup.

stripe-subscriptions

9
from andrelandgraf/fullstackrecipes

Complete subscription billing system with Stripe integration, feature flags for plan gating, webhook handling, and billing portal.