Skip to content

fix: track SvelteDate when read through $state.snapshot - #18699

Closed
Stadly wants to merge 1 commit into
sveltejs:mainfrom
Stadly:fix/snapshot-tracks-sveltedate
Closed

Stadly wants to merge 1 commit into
sveltejs:mainfrom
Stadly:fix/snapshot-tracks-sveltedate

Conversation

@Stadly

@Stadly Stadly commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #18698

Problem

clone() reaches the value instanceof Date branch before the toJSON branch, and structuredClone reads a Date's internal slot rather than going through the prototype. A SvelteDate therefore registers no dependency when read through $state.snapshot, so a reaction never re-runs when the date changes.

SvelteMap and SvelteSet are unaffected, because their branches copy by iteration and so go through the reactive machinery — which is what makes this look like an oversight rather than a deliberate asymmetry.

Fix

Clone as new Date(value.getTime()). The read goes through the prototype, which is what makes the reactive subclass register a dependency, and the result is equivalent to structuredClone for a Date — invalid dates included, since getTime() returns NaN and new Date(NaN) is an Invalid Date.

Moving the Date branch after the toJSON branch would also fix it, but would be breaking: a plain Date would then snapshot to a string.

Test plan

Added a case to packages/svelte/src/reactivity/date.test.ts that reads a SvelteDate through snapshot() inside a render_effect and asserts the effect re-runs on mutation. It fails on main — the log holds only the initial value — and passes with the fix.

Suites run locally:

  • src/reactivity + tests/snapshot — 88 passed
  • tests/runtime-runes — 2699 passed, 47 skipped

`clone()` reached the `value instanceof Date` branch before the `toJSON`
branch, and `structuredClone` reads a Date's internal slot rather than going
through the prototype. A `SvelteDate` therefore registered no dependency when
it was read through `$state.snapshot`, so a reaction never re-ran when the
date changed.

`SvelteMap` and `SvelteSet` were unaffected, because their branches copy by
iteration and so go through the reactive machinery.

Cloning as `new Date(value.getTime())` reads through the prototype, which is
what makes the reactive subclass register a dependency, and is equivalent to
`structuredClone` for a Date, invalid dates included.
@pkg-svelte-dev

Copy link
Copy Markdown

Install the latest version of svelte from 99a356e:

pnpm add https://pkg.svelte.dev/svelte/c/99a356e721f6fbf8e36c14bf3189b5aa261d7738

Open in pkg.svelte.dev: https://pkg.svelte.dev/repos/svelte/pr/18699

Note

This PR is from a fork. A maintainer must approve approve each commit before it can be built and installed.

@changeset-bot

changeset-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 99a356e

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

This PR includes changesets to release 1 package
Name Type
svelte 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

@dummdidumm

Copy link
Copy Markdown
Member

Thank you for the issue and the fix, though I like the solution in #18700 a bit better, therefore closing in favor of that.

@dummdidumm dummdidumm closed this Aug 24, 2026
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.

$state.snapshot does not track SvelteDate, unlike SvelteMap and SvelteSet

2 participants