Serving static files in Electron (React app)
I found another solution without using express
or serve-static
, we only need to cusomize Electron built-in interceptFileProtocol()
to serve static contents.
Code:(main.js)
(I use the electron-quick-start as Electron template)
function createWindow () { window = new BrowserWindow({ width: 800, height: 600 }) window.loadURL(url.format({ pathname: 'index.html', /* Attention here: origin is path.join(__dirname, 'index.html') */ protocol: 'file', slashes: true })) window.on('closed', () => { window = null })}app.on('ready', () => { protocol.interceptFileProtocol('file', (request, callback) => { const url = request.url.substr(7) /* all urls start with 'file://' */ callback({ path: path.normalize(`${__dirname}/${url}`)}) }, (err) => { if (err) console.error('Failed to register protocol') }) createWindow()})
Reference:protocol.interceptFileProtocol()
Explaination:
Normally, if you run React app as a normal website, all static contents should be served by
HTTP [GET]
method. Though they use relative paths, your HTTP server will handle the path parsing work.However, when running under Electron, things change.
Your static contents usually use relative path like
./picture.jpg
, Electron will usefile
protocol instead ofHTTP
protocol and find the file under root path likeC://.//
. So static contents like./picture.jpg
won't be loaded correctly.By customizing
interceptFileProtocol()
, all static contents' requests will be pointed to your working directory instead of Windows(or other OS) root.
Finally, I'm not sure whether it's a good solution for all Electron projects, but if you already have a React
project (or some other SPA) and want to wrap it with Electron, this solution would be fine to use.
As an addition to the great answer from @yeze322 above, here a working sample for all not so familiar with node and electron (like me). It took me some time to find out the correct require statements.
main.js (code from @yeze322 plus required imports)
const { app, BrowserWindow, protocol } = require('electron')const path = require('path')const url = require('url')let mainWindowfunction createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600 }) mainWindow.loadURL(url.format({ pathname: 'index.html', /* Attention here: origin is path.join(__dirname, 'index.html') */ protocol: 'file', slashes: true })) mainWindow.on('closed', function () { mainWindow = null })}app.on('ready', () => { protocol.interceptFileProtocol('file', (request, callback) => { const url = request.url.substr(7) /* all urls start with 'file://' */ callback({ path: path.normalize(`${__dirname}/${url}`) }) }, (err) => { if (err) console.error('Failed to register protocol') }) createWindow()})app.on('window-all-closed', function () { if (process.platform !== 'darwin') { app.quit() }})app.on('activate', function () { if (mainWindow === null) { createWindow() }})
In your main file you have
const app = require("app")app.on("ready", () => { ...
Here you can start the server like you would do in node.js
const serveStatic = require('serve-static') // or const express = require('express') ...}