Adding React to ASP.net MVC 4 with Webpack & Typescript
- Posted by John Ackerman
- On September 3, 2019
- 1 Comments
This blog post is intended for .NET Framework (.NET 4.X). If you’d like to do the same on .NET Core, take a look at this post.
Recently a customer asked us to add some advanced user interface features to a legacy MVC 4 web application. For a number of reasons outside of the scope of this post, we wanted to use React to implement those features. While reactjs.net does a pretty good job of documenting the process, we found the information about adding Webpack and Typescript support a bit lacking.
This blog post will cover, in detail, how we added support for React to our ASP.net MVC web application.
The full source code for this post is available on Github.
Starting Point
This blog post assumes you already have a .NET MVC web app that you want to add react support to. If you don’t then you can create a new MVC Web App by following these steps:
- Open Visual Studio IDE (We use Visual Studio Community 2019)
- Choose File > New > Project
- Find the “ASP.NET Web Application (.NET Framework)” option
- Be sure to select the MVC project template type
Ultimately these directions should work with any .NET MVC 4 project.
Make sure that you have Node and NPM installed. If not, download it from here.
Installing Packages
In order to use reactjs.net in the application, we need to add a few packages from nuget. The packages you should add are:
React.Web.Mvc4
JavaScriptEngineSwitcher.V8
JavaScriptEngineSwitcher.V8.Native.win-x64
JavaScriptEngineSwitcher.V8.Native.win-x86
ReactConfig
You’ll notice that the nuget installation process added a new file in Project > App_Start called ReactConfig.cs. By default it looks like this:
public static class ReactConfig { public static void Configure() { // If you want to use server-side rendering of React components, // add all the necessary JavaScript files here. This includes // your components as well as all of their dependencies. // See http://reactjs.net/ for more information. Example: //ReactSiteConfiguration.Configuration // .AddScript("~/Scripts/First.jsx") // .AddScript("~/Scripts/Second.jsx"); // If you use an external build too (for example, Babel, Webpack, // Browserify or Gulp), you can improve performance by disabling // ReactJS.NET's version of Babel and loading the pre-transpiled // scripts. Example: //ReactSiteConfiguration.Configuration // .SetLoadBabel(false) // .AddScriptWithoutTransform("~/Scripts/bundle.server.js") } }
We’re going to be building our react components with webpack and storing the built files in a folder called wwwroot. In order to support this, remove the existing code from ReactConfig.cs and, instead, use the following snippet:
public static class ReactConfig
{
public static void Configure()
{
ReactSiteConfiguration.Configuration
.AddScriptWithoutTransform("~/wwwroot/dist/vendor.js")
.AddScriptWithoutTransform("~/wwwroot/dist/runtime.js")
.AddScriptWithoutTransform("~/wwwroot/dist/components.js");
JsEngineSwitcher.Current.DefaultEngineName = V8JsEngine.EngineName;
JsEngineSwitcher.Current.EngineFactories.AddV8();
}
}
In order to use these methods, you’ll want to make sure you have the following using statements in your ReactConfig.cs
using React;
using JavaScriptEngineSwitcher.Core;
using JavaScriptEngineSwitcher.V8;
Add Webpack Support
In order to support Webpack, you’ll need to install it in your project. Open a terminal window and navigate to the directory of your MVC project. From there, run the following commands to install webpack.
npm init -y
npm install webpack --save-dev
npm install webpack-cli --save-dev
In Visual Studio, include the package.json and package-lock.json files that were added by the above commands in your project. To do so, right click the project and select “add existing item” and select these two files.
In Visual Studio add a new file to your project called “webpack.config.js”. Copy the following code into the webpack config file:
const path = require('path');
module.exports = {
entry: {
components: './Content/react/expose-components.ts',
},
output: {
filename: '[name].js',
globalObject: 'this',
path: path.resolve(__dirname, 'wwwroot/dist'),
publicPath: 'dist/'
},
mode: "production",
optimization: {
runtimeChunk: {
name: 'runtime', // necessary when using multiple entrypoints on the same page
},
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
},
},
},
},
module: {
rules: [
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
use: [
{
loader: "ts-loader"
}
]
},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader"
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
}
};
To add support for the loaders from this config, in your terminal window, run the following commands:
npm install ts-loader source-map-loader --save-dev
Open the package.json file and replace the “scripts” section with the following:
"scripts": {
"build": "webpack --config webpack.config.js",
"watch": "webpack --watch --config webpack.config.js"
},
Add Typescript Support
In order to include Typescript support in your application, open a terminal window and navigate to your project folder. Within the project folder, execute the following command:
npm install typescript --save-dev
From within Visual Studio, create a new file in your project called “tsconfig.json” and add the following configuration:
{
"compilerOptions": {
"noImplicitAny": false,
"module": "commonjs",
"target": "es6",
"jsx": "react",
"esModuleInterop": true,
"types": [ "./types" ],
"lib": [ "es2015", "dom" ]
},
"include": [ "./Content/react/**/*" ]
}
From within Visual Studio create a new folder in your project called “types” and within that folder add a file called “index.d.ts”. Paste the following code into the index.d.ts file:
import _React from 'react';
import _PropTypes from 'prop-types';
declare global {
const React: typeof _React;
const PropTypes: typeof _PropTypes;
}
Install the type definitions for react by opening a terminal window in your project directory and running the following command:
npm install --save-dev @types/react
In order to get Typescript checking on build, you’ll need to modify your project file. In Visual Studio, right click the project and choose “Unload Project”. Make sure to save any pending changes. Right click the unloaded project again and choose “Edit xxx.csproj”.
Scroll to the first reference to the <Target> element, and add the following build target:
<Target Name="Typecheck" AfterTargets="AfterBuild">
<Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="node_modules/.bin/tsc" />
</Target>
Right click the unloaded project again and choose load project.
Add a React Component
You are now ready to add your first react component. First add references to react and react-dom by opening your project folder in a terminal window and running the following:
npm install react react-dom --save
In your project > Content directory add a new directory called “react”. Within this folder create a file called “expose-components.ts” and add the following code:
import React from "react";
import ReactDOM from "react-dom";
import ReactDOMServer from "react-dom/server";
declare var global: any;
global["React"] = React;
global["ReactDOM"] = ReactDOM;
global["ReactDOMServer"] = ReactDOMServer;
Add a new folder for your component under Project > Content > react called “testComponent” and within that folder add a file called “test.tsx”.
You could write any react component you like here, but for simplicity sake we’ll just create a text box and paragraph that echos what we input. Place the following code in test.tsx:
import * as React from "react";
export class Props { }
export class State
{
name:string
}
export default class TestComponent extends React.Component<Props, State>{
constructor(props) {
super(props);
this.state = { name: ""}
}
handleChange = (ev) => {
this.setState({ name: ev.target.value });
}
render() {
return (
<div className="react-div">
<input type="text" name="name" onChange={this.handleChange} />
<p>Hello, {this.state.name}!</p>
</div>
);
}
}
Modify the expose-components.ts file you created earlier to export this component as well. Modify it to look like this:
import React from "react";
import ReactDOM from "react-dom";
import ReactDOMServer from "react-dom/server";
import TestComponent from "./testComponent/test";
declare var global: any;
global["React"] = React;
global["ReactDOM"] = ReactDOM;
global["ReactDOMServer"] = ReactDOMServer;
global["TestComponent"] = TestComponent;
Add the React Component to a View
The final step is to add the react component to an MVC view. For the simple case, we’re just going to add the component to the home/index view but you could put it anywhere.
In the view, wherever you want the component to be rendered, add the following code:
@Html.React("TestComponent", new { })
At the bottom of the view, add the following lines:
@Scripts.Render("~/wwwroot/dist/runtime.js")
@Scripts.Render("~/wwwroot/dist/vendor.js")
@Scripts.Render("~/wwwroot/dist/components.js")
<!-- Render the code to initialise the component -->
@Html.ReactInitJavaScript()
Test Your Work
To test that your integration worked, first open a terminal window pointed at your project directory. Within the terminal, execute the following code:
npm run watch
This will create a build of your react component and watch the files for any changes. If there are any errors at this step resolve them before moving on.
Start your project from within Visual Studio with or without debugging. Navigate to the view where you added your react component and if everything worked out,you’ll see a textbox that you can modify and a <p> tag that updates as you change the input.
Wrapping Up
This can obviously get a lot more complicated, and in a real world application it would. You can follow just about any React paradigm that you’re used to, to continue from here. You can add packages and reference them, you can create sub-components and just about anything else you’d like.
The one thing to keep in mind is that any other top level components you add, be sure to expose them in your expose-components.ts file.
Once again, if you’re interested in the source code for this post, take a look at our Github repository.
If you have any custom software development needs, reach out to us. We’d love to hear about what you’re working on and see if we can help!
1 Comment