DEV Community

Cover image for Deploying Scalable Multi-Page Web Apps with ASP.NET and Vite.js (Windows & Linux/macOS)
Fussionlabs
Fussionlabs

Posted on

Deploying Scalable Multi-Page Web Apps with ASP.NET and Vite.js (Windows & Linux/macOS)

Creating a modern multi-page web application that combines the power of ASP.NET for backend logic and Vite.js for frontend development offers a fast, modular, and scalable solution. This guide walks you through the setup, starting with the essential dependencies.

Step 1: Dependencies

To get started, make sure you have the following installed:

Required Tools

  • .NET SDK (7.0 or later)
  • Node.js (v18 or later)
  • Vite.js (installed via npm)
  • A code editor like Visual Studio Code
  • Project Structure Your project will be structured like this: /MyApp /ClientApp ← Vite.js frontend /Controllers ← ASP.NET MVC controllers /Models ← ASP.NET Model /Properties ← ASP.NET launch setting /Views ← Razor views (optional) /wwwroot ← Final build path Program.cs Startup.cs or minimal hosting

1: Set Up ASP.NET Core Backend

Create a new ASP.NET Core Web App (MVC or Empty):

dotnet new web -n MyApp
cd MyApp
code .

2. Configure Program.cs to serve static files and fallback to index.html in production:


var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
    app.UseStaticFiles();
    app.MapFallbackToFile("index.html");
}
app.UseRouting();
app.MapControllers();
app.Run();

Enter fullscreen mode Exit fullscreen mode

3. Setup Routing Controller for Client App

Modify Controllers/HomeController.cs as follows:

using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
    [HttpGet("{*url}", Order = int.MaxValue)]
    public IActionResult Index(string url)
    {
        return View();
    }
}
Enter fullscreen mode Exit fullscreen mode

You can remove the default Privacy() action and its view since this will be handled by your SPA.

Step 2: Add WeatherForecast API

  1. Create WeatherForecastController.cs Controller

Should create controller inside

/Controller/Api/WeatherForecastController.cs

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Controllers.Api; // change the namespace

[ApiController]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };
    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }
    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        });
    }
}

Enter fullscreen mode Exit fullscreen mode
  1. Create a Model for the Controller

Should create model inside /Models/WeatherForecast.cs

namespace MyApp.Models // change the namespace

public class WeatherForecast
{
    public DateOnly Date { get; set; }
    public int TemperatureC { get; set; }
    public string? Summary { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Enter fullscreen mode Exit fullscreen mode
  1. Update Razor Views

Clear all inside the /Views/Home/Index.cshtml and add:

@{
    Layout = "_Layout";
}

Modify the _Layout file

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>@ViewData["Title"] - MyApp</title>

  <!-- Only for the React Refresh, for other templates remove this -->
  <script type="module">
    import { injectIntoGlobalHook } from "http://localhost:5173/@react-refresh";
    injectIntoGlobalHook(window);
    window.$RefreshReg$ = () => {};
    window.$RefreshSig$ = () => (type) => type;
  </script>
  <!-- End Refresh -->

  <script type="module" src="http://localhost:5173/@vite/client"></script>
</head>
<body>
  @RenderBody()

  <script type="module" src="http://localhost:5173/src/main.tsx"></script>
  @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Here I use React template in Vite so we need to add the React refresh lines. For other templates we can remove them.

Step 3: Set Up React Frontend with Vite

  1. Create your Vite app:
npm create vite@latest ClientApp
Enter fullscreen mode Exit fullscreen mode

Package name: clientapp
Template: react, react-ts, or vue (your choice)

  1. Navigate and install dependencies:

cd ClientApp
npm i
npm i react-router axios
Enter fullscreen mode Exit fullscreen mode
  1. Recommended Folder Structure

/src
/components ← Header, Footer
/pages ← Home, WeatherList
main.tsx
App.tsx

  1. Setup Axios API: /src/api/weather.tsx
import axios from "axios"; //can use JavaScript Fetch

export const getWeatherForecast = () =>
  axios.get("/api/weatherforecast").then((res) => res.data);
// fetch()
Enter fullscreen mode Exit fullscreen mode
  1. Create Home Page and Weather Page
import { useEffect, useState } from "react";
import { getWeatherForecast } from "../api/weather";

function WeatherList() {
  const [forecasts, setForecasts] = useState([]);

  useEffect(() => {
    getWeatherForecast().then(setForecasts);
  }, []);

  return (
    <div>
      <h2>Weather Forecast</h2>
      <ul>
        {forecasts.map((f, i) => (
          <li key={i}>
            {f.date}: {f.summary} ({f.temperatureC}°C)
          </li>
        ))}
      </ul>
    </div>
  );
}
export default WeatherList;
Enter fullscreen mode Exit fullscreen mode
function Home() {
  return (
    <div className="container mt-5">
      <div className="row justify-content-center">
        <div className="col-md-8 text-center">
          <h1 className="mb-4">Welcome to the Movie Application!</h1>
          <p className="lead">Discover, explore, and enjoy our collection of movies.</p>
          <p>Use the navigation bar to browse through different sections.</p>
        </div>
      </div>
    </div>
  );
}
export default Home;
Enter fullscreen mode Exit fullscreen mode
  1. Configure Routing in /src/App.tsx
import { BrowserRouter, Routes, Route } from "react-router";
import { WeatherList } from "./pages/WeatherList";
import Home from "./pages/Home";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/weather" element={<WeatherList />} />
      </Routes>
    </BrowserRouter>
  );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

Final Step: Run the App

Open two terminals:

Terminal 1: ASP.NET Core Backend

dotnet dev-certs https --trust
dotnet watch run
# for https
dotnet watch -lp https
Enter fullscreen mode Exit fullscreen mode

Terminal 2: React Frontend with Vite

cd ClientApp
npm run dev

Enter fullscreen mode Exit fullscreen mode

In single Powershell based run both at a time


Start-Process -WorkingDirectory ".\ClientApp" -NoNewWindow -FilePath "powershell" -ArgumentList "npm run dev"
dotnet watch run

Enter fullscreen mode Exit fullscreen mode

Summary
You now have a scalable, modern full-stack application using:

ASP.NET Core for APIs and static file hosting
React (or other template) + Vite for fast frontend development
Axios + React Router for API calls and navigation
This setup is easily extendable to include authentication, advanced state management, and deployment pipelines.

Top comments (1)

Collapse
 
lamri_abdellahramdane_15 profile image
Lamri Abdellah Ramdane

Great walkthrough—ASP.NET and Vite together make a super modern stack! On Windows, I often lean on ServBay to quickly spin up .NET, Node, HTTPS, and database services with a single click. Makes testing frontend-backend combos like this effortless.