DEV Community

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

Posted on • Edited on

Developing 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 (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.