Skip to content

Conversation

@techniq
Copy link

@techniq techniq commented Oct 21, 2025

Enhances useSearchParams with Date handling (standard schema compatible) and advanced Zod codec support for custom serialization.

🎯 Key Features

1. Native Date Support

  • Automatic Date parsing: URL date strings are automatically converted to Date objects
  • Two date formats:
    • 'date': YYYY-MM-DD format (e.g., 2025-10-21) - perfect for calendar dates
    • 'datetime': Full ISO8601 format (e.g., 2025-10-21T18:18:14.196Z) - preserves exact timestamps
  • Flexible configuration: Set formats via schema property or dateFormats option
  • Works with createSearchParamsSchema, Zod, Valibot, and Arktype

2. Zod Codec Support (v4.1.0+)

  • Custom bidirectional transformations: Define custom encode/decode logic for any type
  • Advanced use cases:
    • Unix timestamps (compact date storage)
    • Base36 encoded IDs (shorter URLs)
    • Custom date formats
    • Legacy API compatibility
  • Automatic codec detection: Extracts encoders from Zod schemas (including .default() wrapped codecs)
  • Server-side support: Works seamlessly with validateSearchParams

📝 Examples

Date Format Support

const schema = createSearchParamsSchema({
  birthDate: { type: 'date', default: new Date('1990-01-15'), dateFormat: 'date' },
  createdAt: { type: 'date', default: new Date(), dateFormat: 'datetime' }
});
// URL: ?birthDate=1990-01-15&createdAt=2025-10-21T18:18:14.196Z

Zod Codecs

const unixTimestamp = z.codec(z.coerce.number(), z.date(), {
  decode: (ts) => new Date(ts * 1000),
  encode: (date) => Math.floor(date.getTime() / 1000)
});

const schema = z.object({
  createdAfter: unixTimestamp.default(new Date('2024-01-01'))
});
// URL: ?createdAfter=1704067200 (instead of 2024-01-01T00:00:00.000Z)

🔧 Technical Changes

  • Added dateFields, dateFormats, codecEncoders, and codecFields tracking to schema info
  • Implemented extractZodCodecEncoder() for codec detection (handles direct and .default() wrapped)
  • Enhanced serializeValue() to respect date formats and codec encoders
  • Updated extractParamValues() to automatically convert date strings to Date objects
  • Modified value setting logic to encode OUTPUT types to INPUT types before validation
  • Extended validateSearchParams with dateFormats option for server-side consistency

✅ Testing

  • 30 unit tests for date parsing, validation, and serialization
  • 7 integration tests for Zod codec scenarios (date-only, datetime, Unix timestamps)
  • All existing tests remain passing (no breaking changes)

📚 Documentation

Comprehensive documentation added covering:

  • Date format configuration (schema property vs options)
  • When to use dateFormats vs Zod codecs
  • Real-world codec examples (Unix timestamps, date-only, base36 IDs)
  • Server-side usage patterns
  • Comparison table: dateFormats vs codecs

@changeset-bot
Copy link

changeset-bot bot commented Oct 21, 2025

🦋 Changeset detected

Latest commit: ce45dc7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
runed Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cofl
Copy link

cofl commented Oct 29, 2025

(not review, just commenting) Would it be useful to also/instead support devalue? That way users could use the same transport hook as they do for load and remote functions (aside: is there a way we can use that hook automatically? might be a good feature request for SvelteKit.), and support any arbitrary type rather than explicitly Dates and whatever Zod codecs they've written. This would also be useful for Valibot and ArkType users, since they don't have access to Zod codecs.

@techniq
Copy link
Author

techniq commented Oct 31, 2025

Hey @cofl 👋 - devalue might be helpful to replace the current usage of JSON.stringify / JSON.parse to support cyclical references, Map/Set, etc, but the use case of adding codec support (which is optional) is to have more control over the encode/decode process.

For example, with a Date instance, you might want to encode it as:

  • full ISO8601 string (YYYY-MM-DDTHH:MM:SSZ)
  • date-only ISO8601 string (YYYY-MM-DD)
  • epoch seconds (date.getTime() / 1000)
  • epoch milliseconds (date.getTime())

Codecs provide the flexibility to control both directions. This also means adding support for other data types, such as Uint8Array or BigInt, is simply to create the codec and then using it within your schema.

const stringToBigInt = z.codec(z.string(), z.bigint(), {
  decode: (str) => BigInt(str),
  encode: (bigint) => bigint.toString(),
});
const utf8ToBytes = z.codec(z.string(), z.instanceof(Uint8Array), {
  decode: (str) => new TextEncoder().encode(str),
  encode: (bytes) => new TextDecoder().decode(bytes),
});
const zodSchema = z.object({
  num: stringToBigInt.default(0n),
  data: utf8ToBytes,
});

Valibot is also investigating shipping something similar to codecs in the future.

With all that said, codecs are purely optional, including for full and date-only ISO8601 strings (but is needed to support epoch time). This PR also supports Date directly with createSearchParamsSchema for both ISO8601 use cases as well as with standard schema via default (only for full ISO8601, but you can be explicit when you validate to use date-only)

  • Full ISO8601 string
const schema = createSearchParamsSchema({
  startDate: { type: "date", default: new Date() }
});
  • Date-only ISO8601 string
const schema = createSearchParamsSchema({
  startDate: { type: "date", default: new Date(), dateFormat: 'date' }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants