Creating a Modern React Single Page App in ASP.NET MVC

C
3 min readNov 18, 2021

--

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

--

--

No responses yet