← Back to blogYouTube Video

October 24, 2026 • By

How to set up Electron with React, TypeScript and webpack

Can't play the video or having issues? Here's the direct link.

The short version

  • Electron needs two webpack builds: the main process with the electron-main target, and the React renderer with the web or electron-renderer target.
  • In development the Electron window loads the webpack dev server at localhost:3000. In production it loads the index.html that webpack builds into dist.
  • Keep .js in webpack's resolve.extensions even in a TypeScript project, because the packages in node_modules ship JavaScript.
  • Set webpack's mode from NODE_ENV instead of hard-coding it. Webpack bakes the mode into process.env.NODE_ENV at build time, so a hard-coded development mode makes a packaged app look for localhost:3000.
  • Put build tools in devDependencies, because electron-builder copies everything in dependencies into the packaged app.

To set up Electron with React and webpack, you give webpack two configs: one compiles the main process with the electron-main target, and the other compiles the React renderer for the web. In development, the Electron window loads the webpack dev server at localhost:3000. In production, it loads the index.html that webpack builds into dist.

Below I build that starter from an empty folder with TypeScript and Tailwind v4, all the way to an installer you can hand to users. I tested every file on this page on Linux with Electron 44.7 and 45.0, and the finished project is on GitHub at hassanaziz0012/electron-react-starter. The video above builds the same starter on Windows with Electron 37.

Why I built my own starter

I'm building a Discord clone with a Go backend, and I wanted Electron for the desktop app because it's the simplest way I know to ship one. The Electron docs don't say much about using it with React or any other framework, and I need React, because I don't want to repeat the same HTML again and again.

None of the starter templates I found fit. Some weren't in TypeScript, and I hate working without type definitions. Others didn't have Tailwind, and the one that did used Tailwind 3. So I built my own.

Electron's main and renderer processes

Every Electron app has one main process. It runs in Node.js and handles the backend side of the app, including creating windows. Each window then loads a web page in its own renderer process, which is basically a Chromium tab. That's where React runs.

The main process needs Node's modules and Electron's APIs, while the renderer is a web page that gets bundled like any other React app. So they need different builds, and this setup has two webpack configs with two entry files: main.ts and renderer.tsx.

This setup vs Electron Forge vs electron-vite

If you'd rather not wire up webpack yourself, here's how the ready-made options compare:

By hand (this page)Electron Forgeelectron-viteelectron-react-boilerplate
Bundlerwebpackwebpack or Vite (Vite support is marked experimental)Viteelectron-vite
Packagingelectron-builderForge's own makerselectron-builder in its templateselectron-builder
How you startnpm init and the files belownpx create-electron-app@latest with a templatenpm create @quick-start/electron@latestClone the repo
Tailwindv4, set up belowAdd it yourselfAdd it yourselfAdd it yourself

All four work. The by-hand version has more files, but you know what every one of them does.

Install the dependencies

Create a folder for the project and an npm package inside it:

mkdir electron-react-starter
cd electron-react-starter
npm init -y

React and React DOM are the only regular dependencies. Everything else only runs while you develop or build, so it goes in devDependencies:

npm install react react-dom
npm install -D typescript@6 electron electron-builder electronmon webpack webpack-cli webpack-dev-server ts-loader html-webpack-plugin mini-css-extract-plugin css-loader postcss postcss-loader tailwindcss @tailwindcss/postcss concurrently wait-on cross-env @types/react @types/react-dom @types/node

electron-builder copies everything in dependencies into the packaged app, even though webpack has already bundled it, so a build tool in there only makes the installer bigger. In the video I put Tailwind and the CSS loaders in dependencies; here they're dev dependencies.

Install TypeScript 6, not 7. ts-loader calls the TypeScript compiler API, and the TypeScript 7 package doesn't have one, so with 7 the build fails.

The video also installs autoprefixer. Tailwind v4 adds vendor prefixes itself, so you can leave it out.

The project structure

electron-react-starter/
├── public/
│   └── index.html
├── src/
│   ├── styles/
│   │   └── global.css
│   ├── App.tsx
│   ├── global.d.ts
│   ├── main.ts
│   └── renderer.tsx
├── webpack/
│   ├── webpack.main.js
│   └── webpack.renderer.js
├── package.json
├── postcss.config.js
└── tsconfig.json

Webpack writes the compiled files to dist, and electron-builder puts the installer in build. Both get created for you.

TypeScript config

Here's tsconfig.json:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["DOM", "ESNext"],
    "jsx": "react-jsx",
    "outDir": "dist",
    "rootDir": "src",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

"jsx": "react-jsx" is what lets you write JSX at all. Webpack takes care of resolving imports, so "moduleResolution": "bundler" tells TypeScript to resolve them the way a bundler does. The video uses "module": "commonjs" with "moduleResolution": "node", which TypeScript 6 now rejects as deprecated.

TypeScript also needs to know that importing a CSS file is fine. That's src/global.d.ts, which is one line:

declare module "*.css";

Without it, TypeScript 6 stops the build at the import "./styles/global.css" line in the renderer.

The main process

Here's src/main.ts:

import { app, BrowserWindow } from "electron";
import path from "path";

const env = process.env.NODE_ENV;

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
    },
  });

  if (env === "development") {
    win.loadURL("http://localhost:3000");
    win.webContents.openDevTools();
  } else {
    win.loadFile(path.join(__dirname, "../renderer/index.html"));
  }
}

app.whenReady().then(createWindow);

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit();
  }
});

In development the window loads the React dev server and opens DevTools. In production it loads the index.html that webpack builds. __dirname is dist/main once this file is compiled, hence the ../renderer.

In the video I said to check the docs on nodeIntegration and contextIsolation, because I had no idea what they did. With nodeIntegration: false, the web page can't call require or any other Node.js API. With contextIsolation: true, a preload script, if you add one later, runs in a separate JavaScript context from the page. Both are Electron's defaults (since Electron 5 and 12), so you could delete those lines, but I like being able to see them.

The window-all-closed handler quits the app when you close the last window. darwin is macOS, where apps usually stay open with no windows, so it skips the quit there.

The renderer and the React app

src/renderer.tsx mounts React into the page:

import { createRoot } from "react-dom/client";
import App from "./App";
import "./styles/global.css";

const root = createRoot(document.getElementById("root") as HTMLElement);
root.render(<App />);

Import the CSS here. Webpack only builds files that something imports, so without this line your styles never make it into the app. The file has to end in .tsx because it contains JSX; I named it .ts at first in the video and couldn't work out why TypeScript kept complaining.

src/App.tsx is any React component you like. Mine is just enough to show that Tailwind works:

export default function App() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center gap-4 bg-slate-900 text-white">
      <h1 className="text-4xl font-bold">Electron + React</h1>
      <p className="text-slate-300">TypeScript, Tailwind v4 and webpack.</p>
    </main>
  );
}

src/styles/global.css holds the one line Tailwind v4 needs:

@import "tailwindcss";

And public/index.html is the page template, the way Create React App does it:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Electron React Starter</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

The <title> becomes the window title, so change it for your own app. The root div is where React renders.

The main process webpack config

Here's webpack/webpack.main.js:

const path = require("path");

module.exports = {
  mode: process.env.NODE_ENV === "production" ? "production" : "development",
  target: "electron-main",
  entry: "./src/main.ts",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "../dist/main"),
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
};

target: "electron-main" tells webpack this code runs in Electron's main process, so it leaves electron and Node's built-in modules alone instead of trying to bundle them.

Keep .js in extensions. In the video I said you only need .ts and .tsx because the code is all TypeScript, and the app broke. Your own code is TypeScript, but the packages in node_modules ship JavaScript, and webpack has to resolve those too.

Set mode from NODE_ENV too, and don't hard-code it. Webpack replaces process.env.NODE_ENV in your code with the mode at build time. I tried a build with mode: "development" hard-coded, and even with NODE_ENV=production the bundle came out with "development" baked into main.ts. A packaged app built like that would try to load localhost:3000, where nothing is running.

The renderer webpack config

Here's webpack/webpack.renderer.js:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  mode: process.env.NODE_ENV === "production" ? "production" : "development",
  target: "web",
  entry: "./src/renderer.tsx",
  output: {
    filename: "renderer.js",
    path: path.resolve(__dirname, "../dist/renderer"),
    clean: true,
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        include: path.resolve(__dirname, "../src/styles"),
        use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({ template: "./public/index.html" }),
    new MiniCssExtractPlugin({ filename: "output.css" }),
  ],
  devServer: {
    static: path.resolve(__dirname, "../dist/renderer"),
    hot: true,
    port: 3000,
  },
};

The target can be web or electron-renderer. The renderer is a Chromium page with no Node.js access, so either works, and I use web. clean: true empties dist/renderer before each build, so old files don't pile up next to the new ones.

Loaders run right to left, so postcss-loader runs Tailwind on global.css first and MiniCssExtractPlugin.loader writes the result out as output.css last. HtmlWebpackPlugin adds the script and stylesheet tags to public/index.html for you. The devServer block serves the renderer on port 3000 and updates the page when you save.

PostCSS needs one plugin, set in postcss.config.js in the project root:

module.exports = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

The npm scripts

Here's the whole package.json:

{
  "name": "electron-react-starter",
  "version": "1.0.0",
  "description": "Electron + React + TypeScript + Tailwind v4, built with webpack",
  "author": "Hassan Aziz",
  "main": "dist/main/bundle.js",
  "scripts": {
    "start:electron": "electronmon .",
    "dev:main": "webpack --config webpack/webpack.main.js --watch",
    "dev:renderer": "webpack serve --config webpack/webpack.renderer.js",
    "dev": "cross-env NODE_ENV=development concurrently --names \"react,main,electron\" --prefix-colors \"blue,red,magenta\" --kill-others --kill-others-on-fail \"npm run dev:renderer\" \"npm run dev:main\" \"wait-on http://localhost:3000 dist/main/bundle.js && npm run start:electron\"",
    "build:main": "webpack --config webpack/webpack.main.js",
    "build:renderer": "webpack --config webpack/webpack.renderer.js",
    "build": "cross-env NODE_ENV=production npm run build:main && cross-env NODE_ENV=production npm run build:renderer",
    "package": "npm run build && electron-builder"
  },
  "build": {
    "appId": "com.hassanaziz.electronreactstarter",
    "productName": "Electron React Starter",
    "directories": {
      "output": "build"
    },
    "files": [
      "dist/**/*"
    ],
    "win": {
      "target": "nsis"
    },
    "mac": {
      "target": "dmg"
    },
    "linux": {
      "target": "AppImage"
    }
  },
  "electronmon": {
    "patterns": [
      "!src/**",
      "!build/**"
    ]
  },
  "dependencies": {
    "react": "^19.3.0",
    "react-dom": "^19.3.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4.3.3",
    "@types/node": "^26.6.5",
    "@types/react": "^19.3.0",
    "@types/react-dom": "^19.3.0",
    "concurrently": "^10.0.6",
    "cross-env": "^10.1.0",
    "css-loader": "^7.1.5",
    "electron": "^44.7.0",
    "electron-builder": "^26.15.3",
    "electronmon": "^2.0.4",
    "html-webpack-plugin": "^5.6.8",
    "mini-css-extract-plugin": "^2.10.2",
    "postcss": "^8.5.29",
    "postcss-loader": "^8.2.1",
    "tailwindcss": "^4.3.3",
    "ts-loader": "^9.6.2",
    "typescript": "^6.0.3",
    "wait-on": "^9.5.1",
    "webpack": "^5.111.1",
    "webpack-cli": "^7.2.3",
    "webpack-dev-server": "^6.0.0"
  }
}

main points Electron at the compiled main process, so change it from the index.js that npm init wrote.

dev is the long one. cross-env sets NODE_ENV with one syntax that works everywhere, because Windows and Unix shells set environment variables differently. concurrently runs the quoted commands at the end side by side, and --names and --prefix-colors label each line of output so you can tell which one printed it. wait-on holds Electron back until the dev server answers on port 3000 and the main bundle exists, so it never opens on an empty page. --kill-others and --kill-others-on-fail stop everything if one of them crashes, because a React server with no app is useless, and so is an app with no React server.

build sets NODE_ENV to production and runs both webpack configs once. package builds and then runs electron-builder, which reads the build block. appId and productName identify your app, so change both. files tells it the app only needs dist, and the platform keys pick the installer type for each OS.

Reload the main process with electronmon

The dev server only reloads the React side. A change to main.ts gets recompiled by webpack, but Electron keeps running the old code until you restart it.

In the video I used electron-reloader for this. Version 2, released in October 2026, needs import.meta and restarts the app by starting a new Electron process and exiting the old one. When I tried it, the new window ended up detached from npm run dev, and Ctrl+C no longer closed it. electronmon does the same job from a parent process that stays put: the start:electron script runs electronmon . instead of electron ., and it restarts the app when the main bundle changes.

The electronmon block in package.json tells it to ignore src and build. Webpack already turns changes in src into a new bundle or a page reload, and build is electron-builder's output.

Run it

npm run dev

The first run downloads the Electron binary, so give it a minute. You'll see logs in three colors, one per process, and then an 800 by 600 window with DevTools open and "Electron + React" in white on a dark background. Change the heading in App.tsx and save, and the window shows the new text a second later. Change the window width in main.ts and save, and the app restarts at the new size.

Stop everything with Ctrl+C. Closing the window doesn't end the session any more: electronmon keeps waiting for the next change, so you can close the window and edit without restarting npm run dev.

If the React logs show "Module not found: Error: Can't resolve 'scheduler'", check that .js is in resolve.extensions in both webpack configs. That's the error I hit in the video, and Electron shows a page full of errors until you fix it.

Build and package the app

npm run package

This builds both processes in production mode, then electron-builder packages the app into build. On my Linux machine it took under a minute and gave me an AppImage of about 120 MB, most of which is Chromium.

On Windows there are two more steps, from the video. Install Visual Studio (not VS Code) with its build tools, or electron-builder stops with an error telling you to install them. And run the first npm run package from a terminal opened as administrator, since electron-builder creates symbolic links on its first run, and on Windows that takes admin rights. After that, a normal terminal is fine.

While I was recording the video, a new minor version of Electron came out and broke packaging. I spent a long time debugging it, and the fix was to pin Electron back to 37.2.4, the version I'd built the template on. Minor versions aren't supposed to break other packages, but this one did. With the versions on this page, packaging worked first time, but if npm run package suddenly fails after an update, pin Electron to the last version that worked before you dig any deeper.

Build it yourself at least once

Electron, React, TypeScript and Tailwind is my favorite stack, and now I have a starter that's exactly what I want. You could grab a template instead, and for a quick project that's fine. But I'd build it from scratch at least once, because that's how you find out why there are two webpack configs and where the window gets its HTML in production. There are something like 50 steps, and I still kept the old project open next to me as a reference while I recorded.

Video transcript

Show
All right, so in this video, we're going to build a starter app that can use Electron and React, along with TypeScript and Tailwind. We're going to combine all of these different libraries and frameworks and create a development environment that uses all of these together to build a desktop application with Electron. The reason I'm building this is because I'm actually building a personal project, which is what you see right here. This is going to be a clone of Discord, and I'm building the backend in Go. And for the frontend, I just wanted to use Electron because I just wanted an easy, simple way to build a desktop application. Now, when I looked through the Electron docs, I saw that they didn't really have any documentation for integrating with frameworks like React or something. And I really need React because I don't want to repeat the same HTML again and again. I want some sort of a component structure. So I had to use some sort of a framework. Now, there are other starter templates out there, but none of them fulfilled my needs. There were a couple ones they they weren't based on TypeScript. And I really need TypeScript because I just hate working without type definitions There were a couple other ones that did not use Tailwind There was one in particular that used Tailwind but it used Tailwind 3 And I supposed to use Tailwind 4 because that the latest version So there were a couple of problems with all of the other templates and starter templates out there. So I decided to just build my own. And this video is going to be me showing you my process, how I actually built all of this, because I think it's important to know what happens under the hood when you mess around with all of these things. So I'm going to show the entire application here, first of all, and just how it works and everything. And once we're done with that, then I'm going to create this entire application, this entire starter template from scratch right alongside you right here. Now, there's like 50 different steps to create this entire template. So I will be using this whole project as like a reference and just kind of like referencing it again and again throughout the video as I build the new template. So, yeah. But before we even build the entire application template again, first I just want to show you how this even works. So I have a couple of scripts here, npm scripts. And we going to just go through a bunch of them We going to go through dev build and package And these are the main scripts that you going to use when you developing an application so without further ado in this long intro i'm just going to run npm run dev and what this is going to do is it's going to create a development server and run the application in development mode so you can let me just show you So it's going to open a react app in the browser and it's going to then render that react app inside the electron application. So what's going on here is we're rendering all of the UI in this react app and then we're just hosting that react app or you can say rendering that react app inside the electron application. We're literally doing just this. If I show you main.ts, all we're doing is loading the URL localhost 3000 where our react app is posted. And we're loading this inside our application. So that's what's going on. Now, if I just go over to heading here, and I just change the text just to like show you that this does reload instantly in a development environment. So if I change this from small to large, let's say, you see that the React app updates and also the application updates So the development server works fine We can also if I just stop this now and if I run npm run package what this is going to do is it going to package your entire application and build an exe file out of it that you can install and then run on your system just like any other application. So and this is going to take a long time to run. So I will probably just cut this part the video speed it up and yeah what it's going to do is it's going to compile both the main and the render i'll explain both of what both of these are once we actually build this template out but it's going to compile both of these and then it's going to run electron builder which is a separate dependency i'll explain that as well later and it's going to use that to build an application by the way completely off topic but i bought a new camera so the camera should be a lot better now before this i was using my phone and that just you know not not really good so i bought a dedicated camera now and i like it but hopefully my youtube videos get better as well with this Anyway. So, while this is building, let me just show you. In the main application here you can see that when we're in development mode we just run localhost 3000 on our application just to render the react app right but when we're in production mode the react app will build all of its uh ui inside this index.html file in the dist folder right here gonna close that there we go in the disk folder right here and so to render the application in production we just want to load that index.html file we don't like we don't have a localhost server for react running in production so that's what we're doing over here if we're in development mode we load localhost 3000 otherwise we load the final build file that react creates for us and by the way this is great that like it's a great idea to build these things from scratch and just figure out how they work under the hood just so you can like understand what's going on because like in most okay it's done let me just show you so it's going to package your app and put it in this build folder right here and if i just run this this is the setup file it going to just what the hell is that it going to install the application and then we can run it so like i was saying it great to understand how these things work under the hood and there we go this is the application that we built obviously you can create any sort of ui any sort of functionality here this is obviously just a starter template so now you can see that this application does work perfectly fine. Now we can actually go and build this whole thing from scratch. So what I'm going to do is I'm going to go over here and just create a new folder here. And we'll just call it electron react starter app. And then back into VS code, I'm just going to open my code editor in that directory. And then we can start. And actually I am going to create a new window here and just keep the previous project open as well because it did say that I'm going to use that as a reference. But like I said there's like 50 different steps and there's no way in hell that I can keep them all in my mind. So we going to use this as a reference Now the first things we need to do is to install a bunch of dependencies We need let me just recap we need React we need Electron we need TypeScript and we need Tailwind A bunch of these are dev dependencies and some of them are just regular dependencies. So make sure that you're installing the right ones let me just put this side by side so we can actually work with this uh right here okay so in this template the first thing we need to do is actually create uh npm package right so head into your terminal and as soon as this loads there we go just npm init y. The y flag just creates a package and doesn't ask me for any input on different fields. So it's just faster that way. Now, there we go. So we have a package.json here. Now we need to install a bunch of dependencies. So let's go. We need to install a bunch of dev dependencies first. Let's go with typescript. We're going to need that. We're also going to need electron. We're going to need electron builder And this is a separate from electron And what electron builder does is it literally just packages your application and builds exe files and different files that you can like send to your users and they can run and install your application we're also going to need electron reloader and this is what we use for hot reloading so basically this package is going to reload your electron application once you um make some changes so this is what we're going to use in development. We also need to install webpack. And this is just for compiling everything. We need to install webpack-cli. We're going to use this as well. And we need webpack-dev-server. This is what's going to be used to reload our files in development. And this is separate from the electron reloader. So these things are separate. Just keep that in mind. And once we've done all of that, you can see there's a couple more dependencies, but I'm not going to install them right now. We're going to do this step by step. So these are the dependencies that we need right now to get started. We also need to install a couple of types. So install types for electron, for react, for node, and finally for react-gon. Once you've done all of this, hit enter and it will start installing. And this is going to take quite some time to fully install. So yeah, we'll just speed up to that part of the video. All right, so this just finished. We've installed most of our dev dependencies that we need to get started. Next thing we need to do is to install a bunch of regular dependencies. And this is going to be, we need React. We need React DOM. and we also need to get tailwind css and this next one is optional we also want to get tailwind css slash cli this is not really essential because we're not going to use tailwind cli but i just like to have it around just for like debugging purposes if i need to um test something or do something with tailwind directly i like to have it just for that purpose but it's not essential but i am going to install it so up to you whether you want to install this or not um we're also going to need a couple of other things we need to get tailwind css slash post css and this is a post css plugin that we're going to use in webpack to actually compile our tailwind css every time we make a change to our styling we also want to get css loaded again this is a webpack plugin a webpack plugin and this is going to actually compile and create a css file in our distribution folder we also want to have mini css extract plugin and this is another webpack web why do i mess this up webpack plugin that we're going to use for our css and finally auto prefixer and this is another webpack plugin that we're going to use for our css so all of these play some different part in compiling and serving our css files we need all of them so install all of this and wait another 30 minutes to install all right so looks like that's done as well now we have most of the dependency that we need to get started. And you can see they all got added over here to package.json. Now we need to actually create our files and folders. So first of all, create an SRC folder. And this is where all of our code is going to be stored, then create a dist folder. And this is where Webpack is going to compile and put all of our compiled JavaScript, CSS, HTML files, we need both of these So once we have both of these next create a Webpack folder and this is where we going to put our webpack configs so we need two of them we need one for the uh main process and we need one for the renderer so create webpack.main.js and webpack.renderer.js and before we actually set this up we also want to create some code files in here so we need to have main.ts this is going to be the main process and before we go any further the how electron works is it basically has a main process that handles all of the like the back-end stuff and functionality of the app and then it also has a renderer process that handles actually displaying all of the ui and all the elements right so we need to compile both of them separately and that's what we're doing here that's why we have two kind of webpack configs one for main and one for renderer that's why we have a main.ts file here and that's why we're also going to create a renderer file here so i am going to go back to this previous project and just look at how we did it over here because again there is like so many different steps here that i just don want to mess this up so i just going to copy the code from over here and explain everything that I do So first we want to have electron reloader right on the right at the top of our file and what this is going to do is it's just going to again just reload everything and recompile the application every time we make a change. So we do want to this at the top of the file as such then we need to import our electron stuff we need the app and we need the browser window if i can type there we go and then we need the path module as well from path there we go then we need the environment and this is just to separate between the development and the production environment so we want to do something we want to um run localhost 3000 on development and run the index.html file in production just like i showed you here before so that's why we want to access the environment variable and we're going to pass this in our scripts later on. But for now, let's just access it. And you can actually any environment variable in node by just doing process dot env dot the name of the variable. We don't need that console log. Next we need to create the window. So there we go. We'll create a window variable here and just create a new browser window object. I'll give it the same with as I had in my previous project, which is 800 by 600. And then in web preferences, we're going to just put node integration, false, and concepts isolation, false. All that true. If you're wondering what both of these mean, check the docs because I have no idea. but yeah that's what we need so that's our window next we need to actually um set up the environment here and again if we're running in development we want to load a url which will be localhost 3000 this is where the react app is going to be stored localhost port 3000 there we go else if this is a production environment then we want to reload or load path their name and this is just going to give us the absolute path of this um directory and then and this is going to be in the distribution folder by the way in the main folder inside distribution i'll explain that later as well so the actual path here is going to be dash dash slash render and index.html just like you see over here in this different project okay so that's our loading done finally and this is not essential but since we're just testing right now you can actually just open dev tools as soon as you open the application so you can if you remember from the project i showed you earlier when we actually run the application it just immediately opened dev tools right this line is what's doing that you don't need it but i just like to have it around you can of course remove it if you want to so now we actually render this app dot when ready when the application actually launches and it's going to return a promise so we're going to use then and pass in a handler which will be create window app dot all closed so basically when all the windows are closed then create a handler for this and all this is going to do is quit the application and how i did this in my previous project is like such if the process platform does not equal darwin i'm not sure which platform actually is darwin let me just see the options Yeah, so you have a bunch of different options, Windows, Linux, Android, different others. Not sure which one Darwin is, but what we're going to do is just use app.quit. And there we go. So that's all we need for our main.ts file. Next we need to create a renderer.ts file. And this is what's going to actually do all of the rendering and displaying all the elements. So this is where the react code comes in. And let me just show you how I did this before. There we go. So all we're doing is creating root elements over here from the index dot HTML, we'll create that as well. So let just run create roots from react dom slash client There we go And then we just create a root element here which will be document.getElementById root. And this is going to give me a type error. So just call this an HTML element. And then root.render. And this is going to actually render a component. So we need to create the app component like you see over here. So, go over to your explorer and create app.tsx. And this can just be any basic React component like this. What I did in here is I just created an example component here just to show you that we can just continue on adding more and more components or sub-components. So you can do that as well later on. But all we really need is a simple component that just displays something, right? now that we have this, we can actually render this over here. As such, there we go. And it's going to give me an error. Why is going to give me an error. No, actually, wait, we forgot to create the TypeScript configuration. So let's do that first as well so typescript is going to give me an error here because i just haven't specified that we're using react and jsx so let me just show you this is what we need in our typescript configuration so create this file csconfig.json in the root of your project and then just copy what i have over here so we need compiler options and this is going to be target es next I guess this is the next.js ECMAScript. Module will be common.js. Library will be DOM. We're going to need that. And ESNext. JSX will be React.jsx. And this is what actually allows us to use JSX in our code. Otherwise, you get that error as you see. The outer is going to be dist. This is where we actually store all of our files. The compiled JavaScript files. The root there will be SRC. This is where all of our TypeScript code is going to be hosted. Module resolution is Node. We need this so we can import our modules correctly. ES module interrupt. We need this as well to be set to true Strict true just to have better type checking better type checking is always a good thing skip lib check definitely necessary finally include src which is where all of our code is hosted and then we go back and you might need to restart the typescript server to get rid this error it will read the tsconfig.json file and there we go once now now our error is gone so wait never mind i it was a typescript configuration issue but it was also just the fact that i could have named this tsx instead of ts so you probably already know this but if you're creating some sort of jsx file you need to have either a jsx extension or a tsx extension i just forgot to do that there we go so this is done now this is rendering our application and before we move any further we also want to create styles here now i'm going to store this in a styles directory and i'm going to create a global.css file here and we're going to import that in render we need to import this because otherwise it not going to get compiled and packaged up into our application right so the path is style slash global global there we go now it actually going to be loaded and this doesn really do anything if i show you in the original project all it does is it imports tailwind so we can do that here as well import tailwind css and this is just going to create the tailwind um styling that we need we don't really need any more css files because we're just going to render all of this inside tailwind right so yeah now let's also create the index.html file so this is going to be in a public folder it can be in any folder obviously but i just wanted to copy the react way of doing things and then react or at least in create react app you have a public folder which is is where you store all of your assets and your index.html. So that's what we're going to do here as well. We're going to create a public folder and create an index.html file inside it. I messed it up. There we go. HTML, there we go. And it just going to be a completely basic HTML file The title can be anything you want doesn really matter This title is important though Because this is the This is going to be the title of the application Once you actually run this in Electron So obviously for a starter application. You can call it whatever you want. But just keep that in mind. You're going to need to change this. Once you actually create the application. And we just need. Div with the ID of root. This is where the render over here. is going to actually render all of the application. So we just need a div with the id of root here. And that's all we can actually create something, we can actually add a notes for tag as well, just to like, just for completion sake, you can add something like you need JavaScript to run this application. And what this will do is if they have, if the user has JavaScript disabled, for some reason, then this will actually give them this little error message that hey, you need to enable JavaScript to run this application. I don't know if it's actually possible to disable a disabled JavaScript in an electron application. But it's still good to have this message just in case. So we have our HTML, we have our CSS, and we have our um, TypeScript stuff as well. So now we can actually get started on building the web pack configurations. So like I said, we need to have a main configuration. I need to have a render render configuration. So we'll start with main because that's just easier to work with. I go over here. And again, you can just copy everything I have over here. there we go so we're going to need pad this module is going to be very useful because we need to use it to create different kinds of pads um and then we just create module.exports and then just the mode will be development for now target will be electron main and this is for target there are different like options that you can pick i can just show you in the if i have this open uh in the webpack docs webpack docs target so there are different options that you can pick for this target value and these are the different options you can read through them and see what each of these mean but for our purposes all we really need is this one electron main so that what we using here once we have the target next we need the entry file and this is going to be our main like entry file which is going to be in src slash main dot typescript okay then we need the output and this is going to be an object the file name will be bundle.js again this can be anything you want the path will be in pad.resolve we're going to use the path module their name and then outside of this directory because we're inside of a webpack folder then to the dist folder and main there we go so all of this is going to be output it and compile in the dist folder we need a resolve and we're going to resolve these extensions we need to resolve typescript typescript jsx and you don't really need js here but yeah we can just remove that honestly because we're only using typescript right but if for some reason you do use jsx or js in your src folder you need to add those extensions here as well Let close this module This is going to be an object with a bunch of rules inside it And rules is just a list of objects. And here we're going to test for and this is going to be a regular expression. So double dot either a TS file or a TSX file. And then the money sign to actually end the rejects. We're going to use TSLoader. And we actually need to install this by the way. So we'll do that next. And then we need to exclude node modules because no one wants to compile node modules. There we go. So that's all we need for the main. Now let's also install TSLoader. And let me just make sure that this is a dev dependency. I'm pretty sure it is. Yeah. So as I thought, it is a dev dependency. I sometimes get confused whether something is a regular dependency or just a dev dependency. But yeah. We need to install TSLoader. And that's a webpack plugin. And that going to be used to actually compile our TypeScript files into JSX There we go. So now our main Webpack config is done. Now let's build the renderer. And the renderer is going to be a bit more complicated as you can see. And we're going to need to get all those plugins this one we don't really need we need the html webpack plugin and the mini css extract plugin along with css loader post css loader and all the other stuff i showed you before so first of all let's just install all of these right i'm not sure if i if i installed this before but let's just do it all over again we need to get the css loader and these are regular dependencies css loader we need to get the mini css i'm pretty sure i installed these already actually let me just make sure just in case but we need all of these let's install these and yeah i'm pretty sure i installed them before too let me check package.json yeah i think i already installed these so this is redundant yeah it's up to date all Awesome. So I already installed this. But we also need to get the dev dependencies, HTML webpack plugin, post CSS, and post CSS loader. yeah that's all so let's get these as well and if for some reason i miss a dependency we'll just get an error and we'll know what to do next we'll know what dependencies to install so it's not a big deal but let's install these and then we'll create the webpack renderer config which is right here actually while this installs let's get started we obviously need the path module which is in path there we go we need the html webpack plugin what this plugin does is it creates an html file the same as what we have here in index.html in the public folder but what it does is it literally just creates a script file it like injects the bundle that going to be um created by webpack and it just injects it into the html file So basically webpack is going to compile all of our TypeScript into one single bundle file right It just going to load that into the index file I just show you what it does once we actually configure this, and then you'll know what I'm trying to say here so load that and then the mini css extract plugin as well there we go and then just module that exports and let's start writing this config mode for now development entry will be the src renderer file because this is the renderer config renderer.tsx. Target can be web or electron renderer. So again, if I just show you target, you can either keep this as electron renderer or just set it to web. Both of these are fine because electron renderer is also just showing you a chromium web browser. So whether you use the web target or the electron renderer, it's the same thing really. So yeah, I'm just going to use web over here but you can use either of them really and create the output object The file name will be renderer Pat will be pat Their name. And the disk folder. And then the renderer subfolder. Clean will be true. What clean does is it deletes the previous compiled files. And then replaces them with the new compiled files. So if you don't have clean. You'll just build the application. it'll put some files in there in the disk folder, you'll build the application again, and it will just duplicate the files in a sense, you don't need to have that kind of a mess. So we need to have needs to have clean through over here. Then the resolve object. And again, this is going to just have TypeScript and TypeScript JSX, we don't need to have JS or JSX here, because we're just using TypeScript. Module, rules, then we need a list of rules. And first of all, we need to actually compile the TypeScript files. So this is going to just be .tsx, money sign, and there we go. Then the use we going to use tsloader that we already installed And we going to exclude node modules there we go then another rule will there we go so we going to use this rule to load our css now this will just be the css file extensions it's going to include everything in pad.resolve their name and src styles and this is why i wanted to have all the styles inside of one like folder it's going to use a couple of things first the mini css extract plugin.loader then the css loader which is going to create the actual css files and then an object that just loads the poke css loader there we go so there we go right then we actually need to initialize both of these plugins like this we'll create a new html webpack plugin object we'll pass in a template key and this is going to be the path to the index.html file that you have. Now we have it in our public folder. So the path here is going to be public slash index dot HTML. there we go and then we just need to initialize this css extract plugin we pass in a file name and this is going to be the file name of the compile css i just call this output.css there we go awesome and then the actual dev server we need this to just like um for the hot reloading and this is coming from the webpack dev server module that i showed you earlier. So if I go back over here to package.json webpack-dev-server. This is the dependency, the package that we're using to actually set up this dev server. So let's tell it where our static files are going to be. And they're going to be in the pad.resolve their name dist slash render. There we go. Part true, just so it reloads constantly on every change. port will host this on 3000 or whatever you want to host it on and that's all we need now for post css loader here we do need to create another config file so let do that as well let do this outside of webpack we call this post css and we just need to initialize a couple of plugins here So if I show you the postcss config over here this is all we do We just set up module.export and we have plugins key and we just initialize two plugins here. First the Tailwind CSS by postcss plugin. No config necessary. And then the auto-prepixels plugin. Again, no configuration necessary. That's all we need. So now we have our Webpack render config and our main config. And that's all we really need now. So now we can actually start to set up the scripts. So if I go back to package dot JSON in our actual previous application, you'll see I have a bunch of scripts here to do different sorts of things. This is what we're going to create next so in my new project let's head over to package.json remove this test script we don't need it anymore um first we have a tsc script and we don't really need this but what this is going to do is just use use typescript to compile your application now we don really need this i literally do not even use this in the application so Honestly if you just don even want it just remove it It doesn really matter because we don really use this What we do use is a start electron script and this just runs electron Dot is the path where your application is hosted. So that's what we're actually going to use. Then we also need to create the dev scripts and the main scripts and the build scripts, everything. So before we actually do that, if I just close all this off, you can see that the dev command is actually quite long. And we also have a couple of other things to, other packages to install in. First, we need to create a dev main. This is going to start a development server for the main process, the main electron process. so how to do this we just run webpack and we pass in the webpack main config that we created which is in the webpack folder webpack.main.js and we pass in the watch flag and just what this is going to do is just watch for file changes and then recompile the app if we change something in our application that the main then we need the renderer there we go and this is going to compile the renderer webpack config for development mode So again we just use webpack config or webpack serve Config webpack and then just the renderer config. And we need to pass the open flag here just to like open up the server for other people to like access. And once we have that, then we can actually build the dev command. What this is going to do is just run dev main and dev render. But before we do this, we need to install a couple of packages. We need to install concurrently and we need to install wait on. And let me check again if these are dev dependencies or yeah, they are dev dependencies. So we need to get concurrently. What this package does is it allows you to run a couple of programs concurrently. So at the same time, basically, we need to install wait on, which is very helpful to have alongside concurrently, because what wait on does is it like lets you wait for certain files, hosts or other like resources to like become available. So we're going to use this to to wait until our react server is fully available on localhost 3000 and then we're going to run the electron application so we need this and we need crossenv this is a package that we use to pass in environment variables to different commands in like a cross platform way so there are different ways to pass in environment variables to commands in windows in linux in mac so we use this crossing and repackage to just have a cross platform like just one way of doing those things and then this package can translate that to whatever platform we're currently on so we need these three things and these are dev dependencies so make sure to pass in dash d over here then just run this and wait for it to install all right so that's done now we can actually get started on this dev command it's going to be this is a pretty long command so let's just start writing and i'll explain what everything is happening over here so first of all we set the environment variables to development then we run concurrently and this again this is just a package to run different um file different scripts or programs concurrently at the same time we pass in a couple of flags here First names And this is going to be We need to escape these double quotes here. And we'll have three things here. We have the React application, the main application, and then the Electron process. There we go. and then we have the prefix flag if i show you over here prefix colors there we go and this is just to have different like colors for every single process just so it's easier to distinguish between them i'll show you what this looks like in the actual output but uh yeah escape the quotes and just assign some colors to these i just use blue red and magenta you can obviously put whatever color you want over here And then we can actually create a bunch of a pass and a bunch of scripts to run. So again, escape the quotes and run npm run dev renderer. This will be the React application that we first run. And then again, escape the quotes. Go over here. Then we run dev main. Right here npm run dev main and finally once both of these are done now we actually use that wait on package we wait for the localhost 3000 where the react app is stored or hosted to actually become available and once this is done then we use the cross env package right here we use this to pass in the environment variable development and then we run npm run start electron that's all now we let me just get out of here and we pass in a couple more flags the first kill others this means that concurrently will stop the entire application if one of the processes fails and we actually need this because if for instance the react server stops because of an error or something we don't need to have the electron application running right and the same thing in reverse if we have the application shuts down for some reason we don't need the react server running so we pass in the kill others flag and also the kill others on failed flag and it's giving me an error because i installed a bunch of dependencies without refreshing the app so let me just copy this whole thing close this And then refresh And then let just wait a second did I just delete everything? There we go. Let me just copy this whole thing. And then just add it all over again. There we go. So we have all of our dependencies, we have our scripts here. by the way we need to change this main entry point here to what was it this main bundle.js so when we compile the electron application it's going to run the application from this point so this is going to be hosted in this main bundle.js there we go awesome now we have the development side of things all set up so we can actually try and run this and just see if we did everything correctly or not. If I just run npm run dev. And you can see this is why we passed in those Okay, so it failed, but let me just This is why we actually passed in those prefix colors and names blacks. So we can just have different colored logs here just to make things easier. Now, we do have a problem here. We have an error. Alright All right, had a bit of a break there. Let me just run npm run dev again. And we'll see if this works if we've done everything correctly up to this point. And you can see we get different logs, different colored logs for every single process. And we have an error here. There's a typo. Let me go to the pack main right here and change this to actually call this entry that was just a typo we fix that and we run this again and this time it should work I hope and there we go awesome so it's compiling all of the react app stuff right now module not found electron reloader So we do need to fix that as well, but it just ran the React app on localhost 3000. And okay, so we're missing a bunch of modules over here. We need to reinstall them quickly. So and obviously the React app will just the application and Electron will just load the React app which is full of errors right now So that what we see Okay, so that didn't work either. I don't think it's a missing dependency issue, actually. I think it's just that we're not compiling it properly. So let me just check out what we're doing over here. And just make sure that this is the same as what we have over here. I think we actually do need to add JS over here. Yeah, we do need to actually install resolve.js in these files. Why? Because the node modules are in JS. So we do need to actually do this. I think it's going to work this time but let's see and yes it does so okay so that was the problem so and yeah here the application so I messed up I said before earlier in the video that we didn need to have js over here but apparently we do and we need to have this because all of the node modules that we use they going to be in javascript so in order to bundle up the different packages that we use and we need to bundle them up into our application as well so we do need to actually resolve js um extension as well so make sure you add this in your main renderer file and in the renderer file as well the main webpack file and the renderer webpack file so just make sure you do that as well i said earlier in the video that you don't need to do this but you actually do need to do this and we just learned why so yeah with that being said sorry about that but with that being said the development server works and i'll just cancel this now okay so we'll go back to package.json development servers work now we need to actually create the build functionality over here and the way we do that is with this build command and this will just run build main and build renderer again since both of these are different um processes we need to build both of them separately so let create the individual actual build commands and then we create the actual build command after that So first we build the main process which will just be webpack with config of webpack slash webpack dot main dot js then we need build renderer which will be webpack with the config of webpack slash webpack.renderer.js so we literally just pass in the appropriate webpack configs and then just let webpack handle the rest now our build command is going to set the env variable to production instead of development and then it's just going to run both of these build commands. So first we build the main process and then we build the renderer process. And let's try running npm run build now. And what this is going to do is it's going to create a disk folder and fill it with all the stuff that we have over here. So let's finish this. Let's wait for this to load. And you can see we got sort of an error over here but it's not really important because this is just a warning. We don't really need to worry about this at all. And there we go. It's compiled successfully. So in the renderer folder, in the dist folder over here, this is where all of the React stuff is going to be stored. And in the main folder in dist over here, this is going to be where the main Electron stuff is going to be stored. So yeah. And this is what we're going to use to package up our application. now finally we want to create the package command because we've already seen that the development server works the build command works finally we just need a way to package this application into like create exe files that we can like distribute to users right so to do that again we set the environment variable to production and then we run npm run build which is this build command up here this will just build the main and the renderer processes once we've built all of the files and compiled all of them then we run electron builder the package that we installed earlier to build and package this entire application now before running this just a quick couple of caveats when I was first building this template I needed to do a couple extra things to make sure that electron builder worked now you are going to need to do these as well first of all if you're building on windows if you're building an exe file and stuff you're going to need to install visual studio i'll just show it to you right now you need to head over to visual studio right here and you need to download this just click download over here make sure you download visual studio not visual studio code all right you download visual studio and you then um okay there it is and then once this runs you need to install a couple of important components for this run this installer with a couple of commands which i have open somewhere over here i forgot where uh there it is okay so as you know you probably already know this electron the way it displays the application is it creates or it renders the application inside of a chromium web browser right so in order to do that it needs to have a couple of dependencies on Windows and those dependencies are right here so you need to install a bunch of components in Visual studio and you can do all that with these commands i going to run this right in front of you so you can just see how it works let me just open up my downloads folder because that's where all this is going to be hosted let's go to downloads and then the visual studio setup file there we go then we just need to pass in all of these commands to it all right so it's going to take me a while to add all of these but uh actually maybe i can just get chat jibbitdy to uh add this for me because if i just if i just copy paste this it's not going to work because of the line escapes over here and i don't want to bother with actually typing it all on my own so just convert this command into a single line there we go all right so we just copy all of this basically just um run the setup file with all of these flags that all we need to do so this is the visual studio setup then i add all of these Then I run this It going to first download the installer And then it going to already select all the components that we need So you can see it's already downloaded and installed. Then it's going to launch this. And then now I've already installed it. But for you, if you don't have Visual Studio installed with all these components, then it's going to pre-select all of the components for you. the same components that we added over here and then you can just run install and let it do its thing all right so you do need this to like you do need this to compile and package your application so just make sure you have this otherwise you will get an error and it will tell you that you need to install visual studio build tools to package your application and all that so yeah make sure you have all these dependencies installed i'll link this in the the description or something somewhere and you can just check that out as well so yeah there we go and one more thing the first time you run electron builder make sure that you run it as administrator and you can do that by either opening a terminal window in administrator or just opening VS code in administrator and you need to do this Because it's going to create a bunch of semantic links and other stuff that only, for some reason, only admins can do. And it's only going to do that the first time you run this. So the first time you run Electron Builder, just make sure you run it with admin privileges. After that, you don't need to do it anymore. So, with all those caveats discussed, I know this is like a huge barrage of information and it's probably going to cross your head. But yeah, we do need to pay attention to all these things. so with all of that stuff being said let's just run npm run package and see what we get so actually no let me just cancel that let me just cancel it i totally forgot we do need to add some configuration here for the build for the actual build so this is going to be a build key and it's going to be an object let me just add a comma here to fix the error and it's going to have a bunch of stuff here where's the there it is so it's going to have all of these things and this is basically just some configuration properties for electron builder to know what to do to package your app right you can read the electron builder documentation and just figure out all the extra stuff that you can add over here but just to get you started all you really need right now is the app ID I just call this my name plus the application name obviously you can call this whatever you want then the product name which is going to just be electron react starter app again this is just the application name you can call this whatever you want directories the output is going to be in the build directory files so what files it wants to copy so everything that we need to run this program is going to be in the dist folder right so okay this is supposed to be a list not an object make sure you fix that okay so everything that we need is going to be in the disk folder so we just use this syntax to get everything into this folder and then we also specify the bundle.js file more like specifically okay then we need to create the platform specific targets for windows this is going to be nsis for mac this is going to be the dmg files which is how they run applications and for Linux it going to be the app image file that how linux users run electron applications app image there we go so now with all of this done let me just format this because why not with all this done now we can actually run the package command and it should work okay let's run this npm run package and this time it should work. Again, make sure that you the first time you run this, you run this as an administrator and make sure that you have installed Visual Studio build tools if you're running this on Windows. So first it's going to build the application, obviously, just like we specified over here, set the environment variable to production, run the build command, and then run electron builder to package this application. So as you can see over here, the build command successfully ran and compiled everything. Now we start running Electron Builder and now it's going to start packaging the entire application. And you can see it loads all the configuration from the build field inside package.json, the same build field that we added over here just now now packaging the application does take a long time like five or ten minutes so yeah i just going to speed up this this section of the of the video and we'll see what we get in the end because i failed for some reason let me just run it again because i did mess with my connection there so maybe that broke things let's just run it one more time just to confirm so it's going to build the application again and that's obviously going to go really well because in the build command everything already works and okay all right let me just see where I left off you can ignore all of this so we were building a starter template or electron and react and we finished building it but we had just one problem problem, just one small problem with packaging the application. So if I just open that project up again and i can close this one i don't really need this anymore this is a different project so let me just show you the problem again because i spent a lot of time trying to debug this let me run npm run package again and just show you the error one more time because the solution is extremely simple and honestly dumb I'll show you I'll show you what I mean in just a second first let's run this let's see the error that it gives us and then let's fix it don't mind me just checking if my recording is perfect and it seems to be okay so it's building the app and then it will package the app and crash because we haven't fixed the error yet all right so now it's building and packaging it and there we go so this is the error that we're having and it doesn't really matter because the problem is that when i first built this template i was using electron 37.2.4 not 5. and the at the time i started recording this video some minor version came out and it just broke every package for some reason which makes no sense by the way because when you update a minor version of a package from five to six to seven or something or whatever like that it's not supposed to break other packages all right minor version upgrades are not supposed to do that so i don't know why the javascript ecosystem is so broken but what we need to do to fix this is to just install electron version 30 suit 37 so just one minor version behind this this probably just came out like a couple days ago and it just updated it and broke everything so just downgrade to 37.2.4 and then this packaging process should work now i'll run this again and it should work now and once this is done packaging i'll show you the actual app i've actually built this once before while just trying to fix this bug off camera so you can see an exe file over here this is just an old one we're going to build and package this one more time right here and then we're going to run the final app and there we go. It's done. So I'll clear all of this and then I'll go into this build folder where we have packaged our application and we will run this setup file and what this will do is it will just install the application and then run it. So give it a minute and there we go. Now it's installing our application and then it will run and we will see whatever we were rendering there. And there we go. That's our application. That's the final application, the exe file that you can distribute to any of your users and they can just double click and run it. So yeah, that's the final project. We covered how to set up this whole development environment with electron react, TypeScript and Tailwind, which is my favorite stack. And yeah, we set up a development environment, we figured out how to package the whole thing and distribute it and everything else. So yeah, that's about it.

Share this article

Hassan Aziz pointing up

Dude, you’re at the bottom of my landing page.

Just book a call already if you’re that interested.

You scrolled all the way here.