Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dev-warning-optional-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"runed": patch
---

Add dev mode warning when setting fields not found in schema
Original file line number Diff line number Diff line change
Expand Up @@ -2071,6 +2071,16 @@ export function useSearchParams<Schema extends StandardSchemaV1>(
target.set(prop as keyof StandardSchemaV1.InferOutput<Schema> & string, value);
return true;
}

if (import.meta.env?.DEV && typeof prop === "string") {
console.warn(
`[useSearchParams] Field "${prop}" not found in schema.\n` +
`Either:\n` +
` 1. It's a typo (check your schema)\n` +
` 2. It's optional without default (add .default())`
);
}

return Reflect.set(target, prop, value);
},
};
Expand Down
15 changes: 15 additions & 0 deletions sites/docs/src/content/utilities/use-search-params.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ export const productSearchSchema = z.object({
});
```

<Callout type="warning">

**All schema fields must have explicit default values.** Using `.optional()` without a default will cause fields to be silently ignored and not update the URL.

```ts
// ❌ This won't work
z.string().optional()
```

This is because `useSearchParams` extracts field information by validating an empty object against your schema. Fields without defaults won't appear in the result, so the hook won't recognize them as valid parameters.

</Callout>

In your svelte code:

```svelte
Expand Down Expand Up @@ -479,6 +492,7 @@ const searchSchema = z.object({
birthDate: dateOnlyCodec.default(new Date("1990-01-15")),

// Compact product IDs
// Note: .optional() without default works for reading from URL, but won't be writable
productId: compactIdCodec.optional()
});

Expand Down Expand Up @@ -509,6 +523,7 @@ const params = useSearchParams(searchSchema);
// Date-only for event date (more readable in URL)
eventDate: dateOnly.default(new Date()),
// Unix timestamp for filters (more compact)
// Note: .optional() without default works for reading from URL, but won't be writable
createdAfter: unixTimestamp.optional(),
updatedSince: unixTimestamp.optional()
});
Expand Down