Skip to content

Rendering a SPA

You mount a SPA into a Razor page with one of two tag helpers. Both render the container element your app mounts into plus the script/style tags needed to load it — one for development, one for production.

Pages/Index.cshtml
@page
@model YourApp.Pages.IndexModel
@{
Layout = null;
}
<dev-vite-scripts app-name="ReactApp" />

Both helpers take a single attribute:

AttributeRequiredMeaning
app-nameOnly when more than one app is configuredThe SPA directory name to render.

This helper reads manifest.dev.json for the running dev server and:

  • Renders the container <div id="root">.
  • Loads the Vite client and your entrypoint from the dev server (http://localhost:{port}/…), so you get hot module replacement inside the Razor page.
  • For React apps, injects the React refresh preamble first.
  • If the dev server can’t be reached, shows a “Vite Dev Server Not Found” message and retries — so a not-yet-started dev server produces a clear hint rather than a blank page.

This helper reads Vite’s manifest.json (for the hashed bundle paths) and manifest.prod.json (for the container id), then:

  • Emits <link rel="stylesheet"> tags for each CSS file in the entry chunk.
  • Emits the <script type="module"> tag for the hashed JS bundle.
  • Renders the container <div>.
  • If no production manifest is found, shows a “Production Bundle not found” message — a reminder to run npm run build or to check the configured app directory name.

The two helpers are meant to be swapped per environment. A common pattern is to branch on the hosting environment in the page:

Pages/Index.cshtml
@page
@model YourApp.Pages.IndexModel
@inject IWebHostEnvironment Env
@{
Layout = null;
}
@if (Env.IsDevelopment())
{
<dev-vite-scripts app-name="ReactApp" />
}
else
{
<prod-vite-scripts app-name="ReactApp" />
}

For the full attribute and property surface, see the dev-vite-scripts and prod-vite-scripts references.