Hosting a SvelteKit app with ASP.NET Core
Table of contents
- Why Svelte-Kit?
- Setting up the project
- Development Proxy
- Configuring the Svelte-Kit adapter
- Final touches
- Sample code
Why Svelte-Kit?
In this article, we'll create a single-page application (SPA) using Svelte-Kit and ASP.NET Core 7. First, let's talk about the advantages of this setup:
- SEO-friendly: We'll be using static site generation (SSG) together with ASP.NET Core 7, which is good for establishing better SEO scores.
- Super performant: Svelte-kit is known for its performance, which is a win-win for SEO and users.
Setting up the project
- First, we create our project using the SPA template
dotnet new react
. - Next, we don't need the
ClientApp
folder, so we'll start by deleting it. On a *nix terminal, we can userm -rf ClientApp
. - Now, we have everything we need to start installing our SvelteKit app:
npm create svelte@latest ClientApp
cd ClientApp
npm install
Development Proxy
Now, we'll configure the vite proxy for our ASP.NET Core API:
- Look for
launchSettings.json
; In theapplicationUrl
parameter, there are the URLs the application is configured to use in development mode. We can delete the one that uses https because we won't be dealing with SSL for development now. - The http port in this case is 5125, let's configure our SvelteKit proxy to use this port.
Vite allows to proxying requests, so not much work is needed. We can achieve that by adding the following to our vite.config.js
server: {
proxy: {
'/api': 'http://localhost:5125'
}
}
The configuration file now looks like this:
import { sveltekit } from '@sveltejs/kit/vite';
/** @type {import('vite').UserConfig} */
const config = {
plugins: [sveltekit()],
server: {
proxy: {
'/api': 'http://localhost:5125'
}
}
};
export default config;
Svelte-Kit uses Vite, we need to update the command that runs our development frontend in the .csproj
as follows:
<SpaProxyLaunchCommand>vite dev</SpaProxyLaunchCommand>
When running vite dev
, the app runs on port 5173. Let's define that in the csproj
file as well, for our convenience, in the SpaProxyServerUrl
property.
<SpaProxyServerUrl>http://localhost:5173</SpaProxyServerUrl>
Change the WeatherForecastController.cs
route name to [Route("api/controller")]
which will allow us to fetch the dummy data from our SvelteKit app using our proxy configuration.
That's it! We can now run our ASP.NET Core SPA project, with SvelteKit, by using dotnet run
and we can see the app running effectively and the routes being applied.
After setting up our project to run effectively in development mode, let's focus on the publishing part, which is when most of the magic happens, and when ASP.NET Core serves our SPA app.
Configuring the Svelte-Kit adapter
The adapter is responsible for the publishing part of the frontend. You can learn more about it on the Svelte-Kit adapters documentation page for the intricacies of how it functions in a production build. We'll use the following setup:
- Using
build
folder to place all the needed files for the publishing part. - The project is already configured to copy the
build
folder's contents towwwroot
which is where the actual files will be after runningdotnet publish
.
Our adapter of choice is @sveltejs/adapter-static
which is adequate for static-generation. First, install the package using npm install @sveltejs/adapter-static
, then we change the svelte.config.js
file to use this adapter:
import adapter from '@sveltejs/adapter-static';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({assets: 'build', pages: 'build'})
}
};
export default config;
This will tell SvelteKit where to publish the actual content. In our setup, we are using the build folder since it's the folder our ASP.NET Core project already uses for publishing the React contents (line 47).
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)build\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
Remove the Sverdle game from the frontend project. Remove the sverdle folder and remove the link to it in the layout.svelte.
Now, we need to change the build command that the project files use to use Vite instead, so on line 44, change npm run
build to vite build
:
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="vite build" />
Final touches
After the generation, for each route, we'll have a .html file. In our case, for the Index and About pages, everything should work now from the index page, but not from the about page. To have everything working, we must map the route to the file in our Program.cs:
app.MapFallbackToFile("about", "about.html");
Our Program.cs
looks like this now:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("about", "about.html");
app.MapFallbackToFile("index.html");
app.Run();
Sample code
In order to see the complete project used in this example, check out AspNetSPA_SvelteKit GitHub repository.