Skip to content

Fix ?slide=N deep links resetting to the first slide - #79

Merged
PavelMakarchuk merged 2 commits into
mainfrom
claude/vigorous-mayer-a833f2
Oct 13, 2026
Merged

PavelMakarchuk merged 2 commits into
mainfrom
claude/vigorous-mayer-a833f2

Conversation

@PavelMakarchuk

Copy link
Copy Markdown
Contributor

Summary

Loading a deck with ?slide=N (for example /slides/gettsim-2026?slide=5) showed slide 1 and removed the slide param from the URL. This affected every deck.

Cause

SlideshowViewer read the slide param in a mount effect that depended on searchParams. On the first commit, the URL-sync effect ran with currentStep still 0 and called history.replaceState without the param. Next's app router mirrors replaceState into useSearchParams, so the mount effect ran again with no param and reset the deck to step 0.

Fix

  • Read the param once in a lazy useState initializer. The mount effect no longer reads searchParams.
  • Add parseSlideParam() to lib/slide-url.ts. It clamps the step to the deck and falls back to 0 for a missing or malformed value (the old code produced NaN for ?slide=abc).
  • URL updates still go through buildSlideUrl(), so the /slides basePath is preserved.

Tests

  • New components/core/SlideshowViewer.test.tsx mirrors replaceState into useSearchParams the way Next does. 4 of its 5 tests failed on the old code; all pass now.
  • New parseSlideParam unit tests in lib/slide-url.test.ts.
  • Local: bun run test (140 passed), bun run lint (0 errors), bun run typecheck, bun run build all pass on the branch merged with current main.

Manual check (dev server, gettsim-2026, 37 steps)

  • ?slide=5 opens at 6 / 37 and keeps the param (before: 1 / 37, param removed).
  • Arrow keys, Home, and End update the counter and the URL; 10 rapid ArrowRight presses move exactly 10 steps.
  • ?slide=abc opens at step 1; ?slide=999 opens at the last step.
  • No console errors or hydration warnings.

🤖 Generated with Claude Code

PavelMakarchuk and others added 2 commits October 13, 2026 15:47
The viewer read the `slide` search param in a mount effect that also
depended on `searchParams`. On the first commit, the URL-sync effect
ran with step 0 and called history.replaceState without the param.
Next mirrors replaceState into useSearchParams, so the mount effect
re-ran with no param and reset the deck to step 0.

Read the param once in a lazy useState initializer instead, and stop
the mount effect from reading searchParams. Add parseSlideParam to
clamp the step and fall back to 0 for a missing or malformed value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
policyengine-slides Ready Ready Preview Oct 13, 2026 7:49pm UTC

Request Review

@PavelMakarchuk
PavelMakarchuk merged commit 8f7c319 into main Oct 13, 2026
4 of 5 checks passed

This branch was successfully deployed

1 active deployment
Preview – policyengine-slides — d0c9ea76 Deployed Oct 13, 2026 by vercel[bot]
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.

1 participant