Skip to content

Make push_state / replace_state update Turbo's history#339

Draft
myabc wants to merge 4 commits into
marcoroth:mainfrom
myabc:turbo-aware-history
Draft

Make push_state / replace_state update Turbo's history#339
myabc wants to merge 4 commits into
marcoroth:mainfrom
myabc:turbo-aware-history

Conversation

@myabc

@myabc myabc commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Draft — seeking your call on the breaking change before this is merge-ready.

Stacks on #337 / #338. Until #338 merges, this PR's diff also shows that PR's single commit (Register history_forward action); GitHub will trim it automatically once #338 lands. The substantive commits here are the two action changes plus the README update.

Problem

push_state / replace_state drove window.history directly. Raw pushState / replaceState omit the { turbo: { restorationIdentifier } } state key, so Turbo's popstate handler ignores the entry and the browser Back button does not behave as expected (#11). The current workaround is the invoke action.

Change

Route both actions through Turbo.navigator.history.push / .replace (typed, exported API), which set Turbo's state key so restoration works. Relative URLs resolve against document.baseURI. Empty/missing url is a no-op.

Breaking change — your call

The state and title attributes are dropped: Turbo owns its history state ({ turbo: { restorationIdentifier } }) and arbitrary state is unsupported. A test locks in that these legacy attributes are now ignored.

I reused the existing action names rather than adding turbo_* variants. Happy to switch to new names or a non-breaking fallback (keep raw behavior when state is present, Turbo path otherwise) if you'd prefer — flagging before this is merge-ready.

Notes

  • Cross-origin / malformed url raises the same SecurityError the old code did — not a regression, behaves like a normal Turbo navigation. Not handled here; happy to add a guard if you want one.
  • Unit tests stub Turbo.navigator.history and assert the right URL is passed for both actions (absolute + relative), plus no-op cases. Full suite green (202 tests), lint clean.

Closes #11

@myabc myabc changed the title Make push_state / replace_state update Turbo's history Make push_state / replace_state update Turbo's history Jun 27, 2026
myabc added 4 commits June 27, 2026 17:20
history_forward was exported but never wired into registerHistoryActions,
so it never reached Turbo Streams in a real app. Existing tests missed it
because the test helper registers per-action from the Actions namespace
rather than through registerHistoryActions.

Closes marcoroth#337
Raw window.history.pushState omits the {turbo:...} state key, so Turbo's
popstate handler ignores the entry and the Back button misbehaves. Push
through Turbo.navigator.history instead.

BREAKING: the state and title attributes are no longer supported; Turbo
owns its history state. Empty/missing url is now a no-op.

Refs marcoroth#11
Mirror push_state: replace through Turbo.navigator.history so the {turbo}
state key is set and Back works.

BREAKING: state and title attributes dropped; empty/missing url is a no-op.

Refs marcoroth#11
@myabc

myabc commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@marcoroth I've purposely left this in draft since it needs some direction from you. The disadvantage of this solution - as it currently stands - is that it as a breaking change. My take on it though, is that the pain of a breaking change might be worth it if making Turbo Power actions play "nicely" with Turbo Drive out-of-the-box is a goal.

My colleague @dfriquet hit an issue with this downstream in opf/openproject#24076 - so I guess this validates that this is a real world issue! 😄

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.

[Feature Request] Add or improve history actions

1 participant