How to create a React SPA in old school ASP.NET without React.NET with proper bundling and chunk loading
If you’ve ever had to use ASP.NET with a modern frontend stack you’ll notice that it’s actually impossible to find any good information on this. If you can find anything for ASP.NET MVC (not core) it will all be for React.Net which suffers from documentation problems or old version of angular. The main issue of course being the naming between ASP.NET and ASP.NET Core and it being impossible to find anything for ASP.NET Classic
Bad Ideas
Making webpack output one gigantic bundle.js file is a terrible idea. You’ll get dinged by Lighthouse for large initial load times, and as your project grows you’ll have to include every page as part of the initial bundle which isn’t scalable.
Okay Ideas
Manually splitting your project into multiple entry points for each page then including it in the .cshtml
. This is tedious and still not the best way to do things. You can end up with large initial load times
Best Idea
Setup HtmlWebPackPlugin
to output a .cshtml
file that you can include in your main index view. This will autogenerate a partial .cshtml
file with all the auto generated chunked javascript and properly deferred script tags that will load when needed . Along with React Lazy Loading you can get small initial load times and on demand chunk loading.
In the same location as your webpack.config.js create a file called template (no extension. You’ll have to eject create-react-app)
The file contents should be
<%= htmlWebpackPlugin.tags.headTags %>
In webpack.config.js
make your HtmlWebPackPlugin
in the plugin section look like the following
return new HtmlWebPackPlugin({template: 'template',inject: false,minify: false,filename: `${fileName.replace(path.extname(fileName), '')}.cshtml`,});
Inject false stops the plugin from auto creating a full html page and instead creates only the script element with all the Javascript portions included
template points to the template file you created to generate the .cshmtl
script tags
minify: false will stop the plugin trying to incorrectly minify the file which will break things
the filename tag simply uses the existing html filename and appends .cshtml. This will create a file called index.cshtml
(assuming your entrypoint is index.html
). I also have two webpack plugins one outputting .cshtml
and one outputting the traditional html for when I want to develop using the webpack dev server.
When you build your .cshtml output should look similar to the following
<script defer src="/wwwroot/build/590.478c.js"></script><script defer src="/wwwroot/build/index.478c.js"></script><link href="/wwwroot/build/index.bundle.css" rel="stylesheet">
Now go to your ASP.NET project. You’ll probably have some sort of Controller called Home Controller that should load the main page of your asp.net project. Go to the corresponding view for that controller. In the view (probably also called index.cshtml as well) simply include a RenderPage call to your built react project along with a div called root element
@{/**/ViewBag.Title = "Home Page";}<div id="root"></div>@{@RenderPage("~/wwwroot/build/index.cshtml")}
In the shared view, mine simply looks like
<!DOCTYPE html><html><head lang="en" translate="no"><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>@ViewBag.Title</title></head><body>@RenderBody()</body></html>
Client Side Routing
If you use client side routing you’ll notice that if you get off the main route and refresh ASP.NET won’t know what to load.
Simply create a catch all route in your RouteConfig.cs
This looks like
routes.MapRoute(name: "Everything",url: "{*url}",defaults: new { controller = "Home", action = "Index" });}
Now all your routing can be done client side for better or worse and when you reload it will take you to the same spot.
From there, I keep my HomeController
with only one function to load the main page. Then from there I put all the api routes for the frontend into an ApiController with everything under `/api/<action>`.
These are sort of my hastily written thoughts on how to do this, if you have any questions don’t hesitate to email me at medium@calingilan.com