Add SPA routing with custom fetch router outputs - #11629
Open
brophdawg11 wants to merge 3 commits into
Open
Conversation
Contributor
Preview Build AvailableA preview build has been created for this PR. You can install it using: pnpm install "remix-run/remix#preview/pr-11629&path:packages/remix"This preview build will be updated automatically as you push new commits. |
brophdawg11
commented
Jul 23, 2026
| actions: { | ||
| async home(context) { | ||
| await sleep(1000, context.request.signal) | ||
| return <HomePage /> |
Contributor
Author
There was a problem hiding this comment.
actions now return Remix nodes
brophdawg11
commented
Jul 23, 2026
| <code>context.request.formData()</code> without making an HTTP request. History traversals | ||
| return here with GET because navigation entries do not retain <code>FormData</code>. | ||
| </p> | ||
| <form method="POST" action={routes.greet.href()} mix={formStyle}> |
Contributor
Author
There was a problem hiding this comment.
Form submissions are also handled by the SPA router component
brophdawg11
commented
Jul 23, 2026
| } | ||
|
|
||
| export function Layout(handle: Handle<LayoutProps>) { | ||
| let router = handle.context.get(SPA) |
Contributor
Author
There was a problem hiding this comment.
Components can access active/pending URLs from the SPA context
brophdawg11
commented
Jul 23, 2026
Comment on lines
+8
to
+12
| declare module 'remix/router' { | ||
| interface RouterTypes { | ||
| output: RemixNode | ||
| } | ||
| } |
Contributor
Author
There was a problem hiding this comment.
This is the important part to enable returning RemixNode's from the router
markmals
added a commit
to pitlane-tools/templates
that referenced
this pull request
Jul 25, 2026
Switch from a Service Worker fetch handler to the client-side SPA router from remix-run/remix#11629: the router declares RouterTypes.output = RemixNode so handlers return JSX, and the SPA component from remix/ui/spa dispatches navigations and form submissions through the in-memory router. Frames stay: the guest book page is a <Frame name="welcome"> shell whose src resolves through the same router via createRoot's frameInit, with the frame name carried as the x-remix-target request header. Frames only re-resolve when their src changes, so the POST action stamps a freshness token into the src; the sign form is keyed by entry count so submissions mount a fresh form instead of morphing old values. The worker entry, Document shell, render middleware, and @pitlane/dev plugin are gone — the build is a plain Vite SPA (dist + 404.html). pnpm plumbing for the preview build: allowBuilds only matches git-hosted packages by exact resolved commit, so both workspace files pin the remix prepare (true) and decline the prebuilt @remix-run/* scripts (false); blockExoticSubdeps is disabled for the preview's git-tarball dependency graph, and the deploy workflow stops CI's frozen-lockfile default from leaking into the git checkout prepared during install.
brophdawg11
force-pushed
the
brophdawg11/codex-spa-router
branch
from
August 6, 2026 16:07
489d4d7 to
2f2c826
Compare
brophdawg11
changed the base branch from
main
to
brophdawg11/rmx-replace-navigation
August 6, 2026 16:08
brophdawg11
force-pushed
the
brophdawg11/codex-spa-router
branch
from
August 7, 2026 18:57
2f2c826 to
9cc265e
Compare
Assisted-By: devx/755516d2-c45b-45bf-b52b-d964afb579bb
brophdawg11
force-pushed
the
brophdawg11/codex-spa-router
branch
from
August 7, 2026 19:04
9cc265e to
05f7584
Compare
Assisted-By: devx/755516d2-c45b-45bf-b52b-d964afb579bb
Assisted-By: devx/755516d2-c45b-45bf-b52b-d964afb579bb
brophdawg11
force-pushed
the
brophdawg11/codex-spa-router
branch
from
August 7, 2026 19:23
9c3e428 to
d56d265
Compare
brophdawg11
marked this pull request as ready for review
August 7, 2026 19:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
@remix-run/fetch-routercurrently assumes every dispatch produces aResponse, even when the router is used entirely in memory. This adds a configurable output contract and aremix/ui/sparuntime for building browser-only applications whose routers map URLs directly to rendered UI.RouterTypes.outputconfiguration across routers, middleware, controllers, mounted routes, and nestedcontext.router.fetchcalls while retaining the existingstring | URL | Requestinput contract.Requestfor every dispatch, including propagation fromrouter.fetch(url, { signal })tocontext.request.signal.Responseoutput and throws at runtime if downstream middleware returns another value.SPAand the lower-levelcreateSPAsetup utility fromremix/ui/spa. They intercept same-origin Navigation API navigations, cancel superseded loads, expose active and pending URLs, and respectrmx-documentandrmx-history.FormData. By default, same-URL submissions replace history while different-URL submissions push;rmx-historycan override this behavior. History traversals revisit form destinations withGETbecause navigation entries do not retain submitted data.demos/spashowing route maps, controllers, render middleware, pending navigation UI, cancellation, and client-side form submissions.