Suchparameter sind Zustand

von Tanner Linsley am 03. Juni 2025. Suchparameter sind Zustand Header

Suchparameter sind Zustand — Behandeln Sie sie auch so

Suchparameter wurden historisch wie zweitklassiger Zustand behandelt. Sie sind global, serialisierbar und teilbar — aber in den meisten Apps werden sie immer noch mit Zeichenketten-Parsing, lockeren Konventionen und brüchigen Hilfsmitteln zusammengebastelt.

Selbst etwas einfaches, wie die Validierung eines sortieren-Parameters, wird schnell ausführlich

ts
const schema = z.object({
  sort: z.enum(['asc', 'desc']),
})

const raw = Object.fromEntries(new URLSearchParams(location.href))
const result = schema.safeParse(raw)

if (!result.success) {
  // fallback, redirect, or show error
}
const schema = z.object({
  sort: z.enum(['asc', 'desc']),
})

const raw = Object.fromEntries(new URLSearchParams(location.href))
const result = schema.safeParse(raw)

if (!result.success) {
  // fallback, redirect, or show error
}

Das funktioniert, ist aber manuell und repetitiv. Es gibt keine Inferenz, keine Verbindung zur eigentlichen Route und es bricht zusammen, sobald Sie mehr Typen, Standardwerte, Transformationen oder Struktur hinzufügen möchten.

Noch schlimmer, URLSearchParams ist nur Zeichenkette. Es unterstützt keine verschachtelten JSONs, Arrays (über einfaches Aufteilen nach Komma hinaus) oder Typumwandlung. Wenn Ihr Zustand also nicht flach und einfach ist, stoßen Sie schnell an Grenzen.

Deshalb sehen wir einen Anstieg von Tools und Vorschlägen — Dinge wie Nuqs, Next.js RFCs und Userland-Patterns — die darauf abzielen, Suchparameter typsicherer und ergonomischer zu machen. Die meisten davon konzentrieren sich auf die Verbesserung des Lesens aus der URL.

Aber fast keiner davon löst das tiefere, schwierigere Problem: Suchparameter sicher und atomar schreiben, mit vollem Bewusstsein für den Routing-Kontext.


Das Schreiben von Suchparametern ist, wo es auseinanderfällt

Es ist eine Sache, aus der URL zu lesen. Eine andere ist es, eine gültige, absichtsvolle URL aus Code zu konstruieren.

In dem Moment, in dem Sie versuchen, dies zu tun

tsx
<Link to="/dashboards/overview" search={{ sort: 'asc' }} />
<Link to="/dashboards/overview" search={{ sort: 'asc' }} />

Stellen Sie fest, dass Sie keine Ahnung haben, welche Suchparameter für diese Route gültig sind oder ob Sie sie korrekt formatieren. Selbst mit einer Hilfsfunktion zum Stringifizieren erzwingt nichts Verträge zwischen dem Aufrufer und der Route. Es gibt keine Typinferenz, keine Validierung und keine Leitplanken.

Hier wird Beschränkung zu einem Merkmal.

Ohne explizit Suchparameter-Schemata in der Route selbst zu deklarieren, sind Sie auf Raten angewiesen. Sie können an einer Stelle validieren, aber nichts hindert eine andere Komponente daran, mit ungültigem, unvollständigem oder widersprüchlichem Zustand zu navigieren.

Beschränkung ist das, was Koordination ermöglicht. Es ist das, was es nicht-lokalen Aufrufern erlaubt, sicher teilzunehmen.


Lokale Abstraktionen können helfen — aber sie koordinieren nicht

Tools wie Nuqs sind ein großartiges Beispiel dafür, wie lokale Abstraktionen die Ergonomie der Suchparameter-Handhabung verbessern können. Sie erhalten Zod-basierte Parsing, Typinferenz, sogar schreibbare APIs — alles auf eine bestimmte Komponente oder Hook beschränkt.

Sie machen es einfacher, Suchparameter isoliert zu lesen und zu schreiben — und das ist wertvoll.

Aber sie lösen nicht das umfassendere Problem der Koordination. Sie landen immer noch bei duplizierten Schemata, uneinheitlichen Erwartungen und keinem Weg, die Konsistenz zwischen Routen oder Komponenten zu erzwingen. Standardwerte können widersprüchlich sein. Typen können abdriften. Und wenn sich Routen entwickeln, garantiert nichts, dass alle Aufrufer mit ihnen aktualisiert werden.

Das ist das eigentliche Fragmentierungsproblem — und seine Lösung erfordert, Suchparameter-Schemata in die Routing-Schicht selbst zu bringen.


Wie TanStack Router es löst

TanStack Router löst dies ganzheitlich.

Anstatt Logik für Schemata in Ihrer App zu verteilen, definieren Sie sie innerhalb der Route selbst

ts
export const Route = createFileRoute('/dashboards/overview')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']),
    filter: z.string().optional(),
  }),
})
export const Route = createFileRoute('/dashboards/overview')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']),
    filter: z.string().optional(),
  }),
})

Dieses Schema wird zur einzigen Wahrheitsquelle. Sie erhalten vollständige Inferenz, Validierung und Autovervollständigung überall

tsx
<Link
  to="/dashboards/overview"
  search={{ sort: 'asc' }} // fully typed, fully validated
/>
<Link
  to="/dashboards/overview"
  search={{ sort: 'asc' }} // fully typed, fully validated
/>

Möchten Sie nur einen Teil des Suchzustands aktualisieren? Kein Problem

ts
navigate({
  search: (prev) => ({ ...prev, page: prev.page + 1 }),
})
navigate({
  search: (prev) => ({ ...prev, page: prev.page + 1 }),
})

Es ist reduzierer-artig, transaktional und integriert sich direkt in das Reaktionsmodell des Routers. Komponenten rendern nur neu, wenn sich der spezifische Suchparameter, den sie verwenden, ändert — nicht jedes Mal, wenn sich die URL ändert.


Wie TanStack Router Schema-Fragmentierung verhindert

Wenn sich Ihre Suchparameter-Logik im Userland befindet — verstreut über Hooks, Utilities und Helfer — ist es nur eine Frage der Zeit, bis Sie auf widersprüchliche Schemata stoßen.

Vielleicht erwartet eine Komponente `sort: 'asc' | 'desc'`. Eine andere fügt einen `filter` hinzu. Eine dritte geht davon aus, dass `sort: 'desc'` standardmäßig gilt. Keiner von ihnen teilt eine Wahrheitsquelle.

Das führt zu

  • Inkonsistente Standardwerte
  • Kollidierende Formate
  • Navigation, die Werte setzt, die andere nicht parsen können
  • Defekte Deep-Links und Fehler, die Sie nicht verfolgen können

TanStack Router verhindert dies, indem Schemata direkt mit Ihren Routendefinitionen verknüpft werden — hierarchisch.

Elternrouten können eine gemeinsame Suchparameter-Validierung definieren. Kindrouten erben diesen Kontext, fügen ihn hinzu oder erweitern ihn auf typsichere Weise. Dies macht es unmöglich, versehentlich überlappende, inkompatible Schemata in verschiedenen Teilen Ihrer App zu erstellen.


Beispiel: Sichere hierarchische Suchparameter-Validierung

So funktioniert das in der Praxis

ts
// routes/dashboard.tsx
export const Route = createFileRoute('/dashboard')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']).default('asc'),
  }),
})
// routes/dashboard.tsx
export const Route = createFileRoute('/dashboard')({
  validateSearch: z.object({
    sort: z.enum(['asc', 'desc']).default('asc'),
  }),
})

Dann kann eine Kindroute das Schema sicher erweitern

ts
// routes/dashboard/$dashboardId.tsx
export const Route = createFileRoute('/dashboard/$dashboardId')({
  validateSearch: z.object({
    filter: z.string().optional(),
    // ✅ \`sort\` is inherited automatically from the parent
  }),
})
// routes/dashboard/$dashboardId.tsx
export const Route = createFileRoute('/dashboard/$dashboardId')({
  validateSearch: z.object({
    filter: z.string().optional(),
    // ✅ \`sort\` is inherited automatically from the parent
  }),
})

Wenn Sie `/dashboard/123?sort=desc&filter=active` abgleichen, validiert die Elternroute `sort`, die Kindroute validiert `filter` und alles funktioniert nahtlos zusammen.

Versuchen Sie, den erforderlichen Elternparameter in der Kindroute auf etwas völlig anderes neu zu definieren? Typfehler.

ts
validateSearch: z.object({
  // ❌ Type error: boolean does not extend 'asc' | 'desc' from parent
  sort: z.boolean(),
  filter: z.string().optional(),
})
validateSearch: z.object({
  // ❌ Type error: boolean does not extend 'asc' | 'desc' from parent
  sort: z.boolean(),
  filter: z.string().optional(),
})

Diese Art der Durchsetzung macht verschachtelte Routen komponierbar und sicher — eine seltene Kombination.


Eingebaute Disziplin

Die Magie hier ist, dass Sie Ihrem Team keine Konventionen beibringen müssen. Die Route besitzt das Schema. Jeder nutzt es einfach. Es gibt keine Duplikation. Kein Abdriften. Keine stillen Fehler. Kein Rätselraten.

Wenn Sie Validierung, Typisierung und Besitz in den Router selbst bringen, hören Sie auf, URLs als Zeichenketten zu behandeln, und beginnen Sie, sie wie echten Zustand zu behandeln — denn das sind sie.


Suchparameter sind Zustand

Die meisten Routing-Systeme behandeln Suchparameter wie eine nachträgliche Überlegung. Etwas, das man lesen, vielleicht parsen, vielleicht stringifizieren kann, aber selten etwas, dem man tatsächlich vertrauen kann.

TanStack Router kehrt das um. Er macht Suchparameter zu einem Kernbestandteil des Routing-Vertrags — validiert, inferierbar, schreibbar und reaktiv.

Denn wenn Sie Suchparameter nicht wie Zustand behandeln, werden Sie sie weiterhin durchsickern lassen, kaputt machen und um sie herumarbeiten.

Besser, es von Anfang an richtig zu machen.

Wenn Sie von den Möglichkeiten, Suchparameter als erstklassigen Zustand zu behandeln, fasziniert sind, laden wir Sie ein, TanStack Router auszuprobieren. Erleben Sie die Leistungsfähigkeit von validierten, inferierbaren und reaktiven Suchparametern in Ihrer Routing-Logik.