Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/spicy-seas-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@builder.io/qwik-city': patch
---

fix: SPA routing is broken unless origin matches value in in vite.config #8093

If the SSG origin was set to `localhost:3000` and a user visited from `127.0.0.1:3000`, SPA routing would be broken.

Internally, useNavigate's context provider `goto` checks the new destination with the last route location. If the
origin is different, it just does a normal browser navigation. This makes sense; links to other origins cannot use
SPA routing. However, the initial route it compares was using an origin that came from the server environment.

Now, the first navigation will set that initial route to the browser's actual href, eliminating the erroneous
origin mismatch for SPA navigations.
15 changes: 15 additions & 0 deletions packages/qwik-city/src/runtime/src/qwik-city-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,19 @@ export const QwikCityProvider = component$<QwikCityProps>((props) => {
);
const navResolver: { r?: () => void } = {};
const loaderState = _weakSerialize(useStore(env.response.loaders, { deep: false }));

// The initial state of routeInternal uses the URL provided by the server environment.
// It may not be accurate to the actual URL the browser is accessing the site from.
// It is useful for the purposes of SSR and SSG, but may be overridden browser-side
// if needed for SPA routing.
const routeInternal = useSignal<RouteStateInternal>({
type: 'initial',
dest: url,
forceReload: false,
replaceState: false,
scroll: true,
});

const documentHead = useStore<Editable<ResolvedDocumentHead>>(createDocumentHead);
const content = useStore<Editable<ContentState>>({
headings: undefined,
Expand Down Expand Up @@ -218,6 +224,15 @@ export const QwikCityProvider = component$<QwikCityProps>((props) => {
} = typeof opt === 'object' ? opt : { forceReload: opt };
internalState.navCount++;

// If this is the first SPA navigation, we rewrite routeInternal's URL
// as the browser location URL to prevent an erroneous origin mismatch.
// The initial value of routeInternal is derived from the server env,
// which in the case of SSG may not match the actual origin the site
// is deployed on.
if (isBrowser && routeInternal.value.type === 'initial') {
routeInternal.value.dest = new URL(window.location.href);
}

const lastDest = routeInternal.value.dest;
const dest =
path === undefined
Expand Down
Loading