Skip to content

SolidStart 2.0 (alpha) ignores Vite base for prerendered/SSR script and asset URLs #2151

@bigmistqke

Description

@bigmistqke

written by claude

Summary

Under any non-root Vite base (e.g. base: "/repo/" for a GitHub Pages subpath deploy), @solidjs/start@2.0.0-alpha.* produces SSR HTML whose <script>, <link rel="modulepreload">, and <link rel="stylesheet"> tags are root-absolute (/_build/...) instead of base-prefixed (/repo/_build/...). The page returns 200 from the right URL, but every asset 404s, hydration never starts, and the app appears blank.

There's also a secondary papercut: @solidjs/vite-plugin-nitro-2 defaults Nitro's baseURL to / regardless of Vite's base, so users have to write the prefix twice — once as base in their Vite config and again as nitroV2Plugin({ baseURL: "/repo/" }) — to get Nitro to prerender and serve at the right path.

Reproduction

  1. Scaffold a SolidStart 2.0 project, set base: "/sub/" in vite.config.ts.
  2. pnpm buildnode .output/server/index.mjs.
  3. curl http://localhost:3000/sub/ → HTML returns, but every asset reference is /_build/... instead of /sub/_build/....

Where the URLs are emitted

  • packages/start/src/server/manifest/prod-ssr-manifest.ts:
    • path(id)join("/", viteManifestEntry.file)
    • getAssets(id)href: "/" + asset
    • json()output: join("/", ...)
  • packages/start/src/server/manifest/dev-client-manifest.ts:
    • import(join("/", id)) (same bug in dev under a base)
  • packages/start-nitro-v2-vite-plugin/src/index.ts:
    • resolvedNitroConfig has no baseURL, so Nitro defaults to /.

The dev-ssr manifest already does the right thing (normalize(join(import.meta.env.BASE_URL, id))), which is the pattern to copy.

v1 comparison

In v1, Vinxi owned the build manifest and emitted output.path with the router base already prefixed, so StartServer could read it raw and it Just Worked. The v2 migration off Vinxi dropped that wiring. The fix isn't new API — it's restoring base-awareness in the two spots that lost it.

Proposed fix

PR linked below. Three small commits:

  1. Failing unit tests for prod-ssr-manifest.
  2. Replace "/" with import.meta.env.BASE_URL in the prod SSR manifest and the dev client manifest (using pathe.join, which collapses to /foo when base is /).
  3. Default nitroConfig.baseURL to the resolved Vite config.base in the nitro-2 plugin. User-supplied baseURL still wins.

Out of scope (follow-up)

<Router> in the user's app.tsx still requires manual base={import.meta.env.BASE_URL.replace(/\/$/, "")}. Auto-passing it from StartClient/StartServer would close the loop but touches the router integration and is worth a separate discussion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions