Skip to content

[Bug]: Special characters in story controls break state in URL #33853

@hdodov

Description

@hdodov

Describe the bug

Currently, if I have a story with a text control, I can write e.g. My text and the state correctly gets preserved in the URL:

http://localhost:6006/?path=/story/button--default&args=children:My+text

…but if I add an exclamation point ! or any other special character to the value, i.e. My text!, for example, the URL gets cleaned up:

http://localhost:6006/?path=/story/button--default

This sucks because the state is no longer preserved in the URL and sharing it with others wouldn't reproduce the same story. Here's a video:

Screen.Recording.2026-02-16.at.20.30.16.mov

Reproduction link

https://github.com/OblikStudio/test-storybook/tree/issue-url-special-chars

Reproduction steps

  1. Clone the reproduction repo: https://github.com/OblikStudio/test-storybook/tree/issue-url-special-chars
  2. Open the main story: http://localhost:6006/?path=/story/button--default
  3. In the children control, write My text
  4. The story URL will be updated as expected
  5. Add an exclamation point, i.e. My text!
  6. The state is no longer preserved in the URL

System

System:
OS: macOS 15.7.3
CPU: (10) arm64 Apple M4
Shell: 5.9 - /bin/zsh
Binaries:
Node: 22.21.0 - /opt/homebrew/opt/node@22/bin/node
npm: 10.9.4 - /opt/homebrew/opt/node@22/bin/npm
pnpm: 10.19.0 - /opt/homebrew/bin/pnpm <----- active
Browsers:
Chrome: 145.0.7632.76
Safari: 26.1
npmPackages:
@storybook/addon-docs: 10.2.7 => 10.2.7
@storybook/nextjs: 10.2.7 => 10.2.7
storybook: 10.2.7 => 10.2.7

Additional context

This appears to be an encoding issue, so I think that a URL-safe Base 64 encoding, as defined in RFC 4648 would be the perfect solution.

Instead of putting children:My+text! in the URL, you can encode it to Y2hpbGRyZW46TXkrdGV4dCE-, ending up with this perfectly valid URL:

http://localhost:6006/?path=/story/button--default&args=Y2hpbGRyZW46TXkrdGV4dCE-

The good thing is that you can basically have absolutely anything represented this way. The args overrides could straight up be stringified as JSON, then encoded with Base 64, and you'd have no problems. You just decode the Base 64 string, parse the JSON, and that's it.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions