react-router icon indicating copy to clipboard operation
react-router copied to clipboard

[Bug]: v7pre - Can't use vite preview with SPA + basename + prerender

Open daniharo opened this issue 1 year ago • 6 comments

What version of React Router are you using?

7.0.0-pre.0

Steps to Reproduce

  • Enable SPA mode (ssr: false)
  • Set the basename to /chat/
  • Enable prerender.
  • Build the app with npm run build
  • Run vite preview.

Minimal reproduction (run npm run build + npm run preview on it): https://stackblitz.com/edit/react-router-v7-basename-prerender-preview?file=vite.config.ts

Expected Behavior

When opening http://localhost:4173/chat/, the preview server responds with /build/client/chat/index.html and the root route is rendered.

Actual Behavior

There is a 404 error response. When opening http://localhost:4173/chat/, vite tries to respond with the file /build/client/index.html which doesn't exist (the prerendered route is in /build/client/chat/index.html), so there is a 404 response.

daniharo avatar Oct 06 '24 15:10 daniharo

For now I've solved it adding this plugin to vite config, but it doesn't seem very clean

    {
      name: "configure-preview-server",
      configurePreviewServer(server) {
        return () => {
          server.middlewares.use((req, res, next) => {
            if (req.url === "/index.html") {
              req.url = `${req.originalUrl?.slice(0, -1)}${req.url}`;
            }
            next();
          });
        };
      }
    }

daniharo avatar Oct 06 '24 15:10 daniharo

I'm not quite sure what to do here - with basename: '/chat/', React Router has to generate the file inside of a chat/ directory so it can be deployed to a static file CDN and served at chat/.

But then when Vite's base is also set, Vite strips that from any incoming request during vite preview it seems and hence the 404. You need to go to /chat/chat to get Vite to serve the file, and then that fails to hydrate because the basename doesn't match.

I think vite preview works as expected if you just use basename and skip base - is that an option is your setup?

brophdawg11 avatar Oct 10 '24 20:10 brophdawg11

It does work but scripts will have href="/assets/...". I need the href for scripts to begin with /chat (such as href="/chat/assets/entry.client-BmgBuKBB.js"), not only the routes. AFAIK the only way to do that is setting base: '/chat/'.

daniharo avatar Oct 10 '24 22:10 daniharo

I think we have a check somewhere that basename has to start with base so in theory we could strip any base from basename when we generate the files, but that would mean you can no longer run your app via a simple HTTP server like npx http-server build/client because they wouldn't live in /chat anymore and http-server doesn't know to add /chat since that's a vite preview only thing...

I'll see if I can find some time to play around with these combinations and see if there's a good solution that works for all cases.

brophdawg11 avatar Oct 11 '24 13:10 brophdawg11

It's worth mentioning that Vite allows using base: "./" which I've found works quite well for static/prerendered sites. All assets are looked up relatively, avoiding any issues with deeply-nested absolute paths.

mayank99 avatar Oct 22 '24 22:10 mayank99

I'm not quite sure what to do here - with basename: '/chat/', React Router has to generate the file inside of a chat/ directory so it can be deployed to a static file CDN and served at chat/.

@brophdawg11 I think this assumption might be incorrect? The base name is often used for deploying to a sub-directory that already exists. This is the case with GitHub Pages, where you are basically forced to use a sub-path, even if you deploy to the root.

Currently, using reactRouter({ basename: "/chat/", prerender: true, ssr: false }) will generate two directories: build/client/chat/ and build/client/assets/. This makes it very difficult to deploy. It would be easier if the assets/ directory was inside chat/.

~~Maybe unrelated: I also noticed a strange issue after deploying the build/client/ directory as-is. Prerendered pages work when visiting the URLs directly, but don't work at all navigating on the client using <Link>.~~ Edit: This is indeed unrelated; gh-pages was ignoring the Remix __manifest file by default (see more).

mayank99 avatar Oct 22 '24 22:10 mayank99

Don't know if this is the right discussion, however with the following config

// react-router.config.ts
import type { Config } from "@react-router/dev/config";

export default {
	// Disable SSR since we want static pre-rendering
	ssr: false,
	basename: "/syb-interview-ai/",
	// Pre-render all routes at build time
	async prerender() {
		return ["/", "/user"];
	},
} satisfies Config;
// vite.config.ts
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [reactRouter(), tsconfigPaths()],
  base: "/syb-interview-ai/",
});

I get this when running build

Image

There's no way this will work, and i have to move files manually to get to the proper paths

Image

To make it work i have to move the favicon and the assets manually in the syb-interview-ai folder. You also have to be sure that the base in vite ends with a slash otheriwise the build produces paths like syb-interview-aiassets. This slash thing is also a problem because it means that you can't make the website work without a trailing / in the url because react router won't render anything.

As a side note i don't even understand what that __spa-fallback.html should be needed for.

raythurnvoid avatar Feb 19 '25 20:02 raythurnvoid

I'm not quite sure what to do here - with basename: '/chat/', React Router has to generate the file inside of a chat/ directory so it can be deployed to a static file CDN and served at chat/.

@brophdawg11 I think this assumption might be incorrect? The base name is often used for deploying to a sub-directory that already exists. This is the case with GitHub Pages, where you are basically forced to use a sub-path, even if you deploy to the root.

Currently, using reactRouter({ basename: "/chat/", prerender: true, ssr: false }) will generate two directories: build/client/chat/ and build/client/assets/. This makes it very difficult to deploy. It would be easier if the assets/ directory was inside chat/.

~Maybe unrelated: I also noticed a strange issue after deploying the build/client/ directory as-is. Prerendered pages work when visiting the URLs directly, but don't work at all navigating on the client using <Link>.~ Edit: This is indeed unrelated; gh-pages was ignoring the Remix __manifest file by default (see more).

this is actually achievable, what you need to do is set build.assetsDir in vite.config.ts to your basename (but without the slashes) while keeping the base unset.

skrhlm avatar Jun 11 '25 12:06 skrhlm

this is actually achievable, what you need to do is set build.assetsDir in vite.config.ts to your basename (but without the slashes) while keeping the base unset.

I was already setting build.assetsDir to a subdirectory under basename. It fixes the asset paths, but I ran into some really strange issues (e.g. stuff missing in .html) which I've honestly struggled to understand and gave up after a while.

This conversation might be better suited for #13615.

mayank99 avatar Jun 11 '25 12:06 mayank99

🤖 Hello there,

We just published version 7.8.0-pre.3 which involves this issue. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

github-actions[bot] avatar Aug 07 '25 16:08 github-actions[bot]

🤖 bad bot - this is not in 7.8.0 and will be in the next release

brophdawg11 avatar Aug 07 '25 17:08 brophdawg11

🤖 Hello there,

We just published version 7.8.1-pre.0 which involves this issue. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

github-actions[bot] avatar Aug 14 '25 14:08 github-actions[bot]

🤖 Hello there,

We just published version 7.8.1 which involves this issue. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

github-actions[bot] avatar Aug 15 '25 19:08 github-actions[bot]

Hello there, I am running into this same issue, not sure if someone has any workaround? Using v7.8.1

oYazmat avatar Aug 28 '25 00:08 oYazmat

Hello there, I am running into this same issue, not sure if someone has any workaround? Using v7.8.1

Hello, hmm. My issues were resolved after the fix in 7.8.1, make sure your base in vite.config.ts is unset and set the build.assetsDir to your react-router.cofig.ts basename (without the first slash). And that all your pages are in the prerender config.

skrhlm avatar Aug 28 '25 07:08 skrhlm