Loading images can be tricky to load in Electron apps. In development environments, we want a hot-reload server, something that will recompile our application if we make changes. In production environments, we would like all of our code to be bundled and distributable. Are we able to load images in a way that is agnostic of the target environment? There are many others asking this same question, and it turns out we can load images regardless of the environment in an Electron application.
💡
There are some varying suggestions that are definitely not secure, or slightly more complicated than they need to be. Your mileage may vary if you are looking for an up-to-date example online!
In order to support loading images in development and production environments, we will leveraging Webpackand custom protocols. I am assuming as part of this blog post that you are using the Webpack dev server to host/rebundle your application when developing/testing locally.
👍
A working Electron application template with the changes proposed in this blog post can be found here.
Configure your Asset Modules
Starting in Webpack 5, Asset Modules are the new way that replace the raw-loader , url-loader and file-loader. We will be configuring an Asset Module in order to load images (if you are using a version of Webpack prior to version 5, use the url-loader in your configuration file to load images). Add an entry in your module.rules array property to identify image files.
sample webpack.config.js file
The important bits to call out here are:
The test property needs to include any image file extensions you may be using in your application.
The include property should point to the directory all of your images are saved in.
The type property should be set to "asset/inline", this exports a data URI of the asset within the bundled Webpack output.
Once the Webpack configuration file has been updated, you can import your image in any of your front-end pages
sample React front-end component
These changes will allow you to load images when running your Electron application in non-prod environments, but as soon as we want to run our Electron in a production build, we will see that our solution does not allow us to load images. In order to support loading images in a production build of an Electron application, we need to implement a custom protocol.
Implement a custom protocol
Electron application's UIs are loaded through BrowserWindows. By default, a BrowserWindow can't load local resources unless the webPreferences.webSecurity property is set to false. The webSecurity property is set to true by default to prevent the UI to load any local resource; this is a good default when considering security, but how can we load images while still keeping our application secure?
We can keep our application secure by defining a custom URI scheme that our application loads resources over. It is insecure to load resources over the file:// scheme because in certain situations the CSP can be bypassed. By default, an Electron app will load resources over the file:// scheme, so in order for our applications to stay secure and allow images to load, we need to configure our Electron app to load resources over a different scheme. By defining our custom protocol as standard (shown below, in an image), we allow our protocol to "allow relative and absolute resources to be resolved correctly when served. Otherwise the scheme will behave like the file protocol, but without the ability to resolve relative URLs." (https://www.electronjs.org/docs/latest/api/protocol#protocolregisterschemesasprivilegedcustomschemes).
Fortunately, a great protocol was written by moloch– that we can leverage in our Electron applications.
protocol.js
DIST_PATH and/or scheme may be modified here to your liking/needs. The DIST_PATH needs to be the same directory that Webpack will output your bundled files in a production-environment build.
Once we've added this file into our application, we need to configure the protocol in our Electron application's main.js file.
main.js
Assuming you too are using the HtmlWebpackPlugin, you will need to configure the base property (which injects a <base> tag in your HTML).
Setting the base property defines a base URL for all relative URLs. By setting the <base> tag to our custom scheme, our app will now properly load images in a production build.
Github
A sample project that has these changes can be found at the secure-electron-template repository.