Skip to content

Instantly share code, notes, and snippets.

@bbudd
Created June 13, 2019 12:30
Show Gist options
  • Star 43 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bbudd/2a246a718b7757584950b4ed98109115 to your computer and use it in GitHub Desktop.
Save bbudd/2a246a718b7757584950b4ed98109115 to your computer and use it in GitHub Desktop.
Loading static assets using electron-forge v6 and the webpack plugin

I spent a few hours chasing down just how to get my static assets (css, fonts, images) to load in an Electron app built using electron-forge v6 (https://www.electronforge.io/) and its webpack plugin (https://www.electronforge.io/config/plugins/webpack) while in development mode. There really isn't any documentation available online, either in the electron-forge documentation or in places like blogs or gists or stackoverflow. So I thought I'd put it down here.

Step 1

Load CopyWebpackPlugin npm i -D copy-webpack-plugin


Step 2

Use the plugin to move your directories into place.

It's important that they be at bare minimum in .webpack/renderer, since that is the root used for localhost

webpack.renderer.config.js

const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const assets = [ 'img', 'css', 'fonts' ]; // asset directories

module.exports = {
  // Put your normal webpack config below here
  module: {
    rules: require('./webpack.rules'),
  },
  plugins: assets.map(asset => {
    return new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, 'src', asset),
        to: path.resolve(__dirname, '.webpack/renderer', asset)
      }
    ]);
  })
};

Step 3

IMPORTANT: Access files with absolute paths

Files without absolute paths do not appear to work, even when everything is relatively placed.

index.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <link rel="stylesheet" href="/css/main.css">
  </head>
  <body>
    <img src="/img/img_src.png" />
    <p style="font-family: MyFancyFont, sans-serif;">👆 that's an image!</p>
  </body>
</html>
@pearson1933
Copy link

Any advice on how to import static assets in react components?

@Kvisaz
Copy link

Kvisaz commented Apr 2, 2020

Thank you.

2020-04-20 - I test relative path for assets in Electron 6.0.0 (Windows 8 x64) - it is working

2020-04-02_131610

@dxinteractive
Copy link

dxinteractive commented May 9, 2020

Hi @bbudd, thanks for posting this. Electron forge webpack docs pretty much don't exist for anything deeper than just webpacking some js files. Using version 6.0.0-beta.51

Your approach almost worked for me, but using absolute paths didn't quite work once I run make on OSX. Say if I had an image loaded at /image.png, running start is fine and the image would load from localhost:3000/image.png... but the file produced by make would attempt to load the image from file:///image.png and then fail to find anything. I assume that something in the make process would need to know the file location that corresponds to requests to /, but I can't work out where that happens, or how to intercept requests.

Not sure if you'll reply, but did you have any issues like this?

EDIT: found a lead, I might be right from here on in electron/forge#1592

@dougdenn
Copy link

dougdenn commented Jun 2, 2020

Thanks for this, I was banging my head on this for a few hours and your post helped me out.

@paragbaxi
Copy link

@vasani-arpit
Copy link

I resolved this without using CopyWebpackPlugin in my case I had issue with FontAwesome. I tried to take solutions from https://stackoverflow.com/questions/33649761/how-do-i-load-font-awesome-using-scss-sass-in-webpack-using-relative-paths but none of them worked.

so, I imported/set the fonts and sass like this in app.scss file

$fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
@import "~@fortawesome/fontawesome-free/scss/solid";
@import "~@fortawesome/fontawesome-free/scss/brands";
@import "~@fortawesome/fontawesome-free/scss/fontawesome";

but this wasn't enough for icon to show up the icon was coming up as □ in the web page.

then I found out from the docs that I need to write extra class for it. so I added the following in app.scss

.product {
    @extend %fa-icon;
    @extend .fas;

    &:before {
        content: fa-content($fa-var-store);
    }
}

and it worked !!!

This might be trivial for most of you but I am new to scss and webpack so I thought I should share because others might need it.

@anontyro
Copy link

anontyro commented Nov 9, 2020

This is awesome it worked wonders for me, the only issue I had was using a newer version of copy-webpack-plugin so I had to use a slightly different way to wrap it:

const copyPlugins = assets.map((asset) => {
  return new CopyWebpackPlugin({
    patterns: [{ from: path.resolve(__dirname, 'src', asset), to: asset }],
  });
});

it requires the patterns wrapping now and then it works this was very helpful

@johannesgiani
Copy link

Another way could be to use webpack file-loader. I use it to import icons in the main process but that should work for a renderer as well.

Given a /src/renderer.ts/js and /assets/icon.png:

import icon from '../assets/icon.png';

add a rule in webpack.rules.js:

//...
  {
    test: /\.png$/,
    use: {
      loader: 'file-loader',
    }
  }
//...

@nkallen
Copy link

nkallen commented Apr 9, 2021

I've got this working in both dev and prod using file-loader. Here's what I've done:

webpack.renderer.config

rules.push({
    test: /\.(png|jpg|svg|jpeg|gif)$/i,
    use: [
        {
            loader: 'file-loader',
            options: {
                name: 'img/[name].[ext]',
                publicPath: '../.'
            }
        },
    ],
});

I needed to set publicPath because in prod files are relative to your entryPoints.name (which is "main_window" by default with electron-forge) but the asset files are copied into render not render/main_window

In a random js/ts file:

import porcelain from './img/matcap-porcelain-white.jpg';

And my file is located in '/src/img/matcap-porcelain-white.jpg

@jin-yee
Copy link

jin-yee commented Apr 20, 2021

Thanks for the help!

It works fine, for npm 7 and later please downgrade the npm to 6 and copyWebpackPlugin please use the following configuration:

 plugins: assets.map(asset => {
    return new CopyWebpackPlugin(
      {
        patterns: [
          {
            from: path.resolve(__dirname, asset),
            to: path.resolve(__dirname, '.webpack/renderer', asset)
          }
        ]
      }
    );
  })

@DK013
Copy link

DK013 commented Jun 19, 2021

I've got this working in both dev and prod using file-loader. Here's what I've done:

webpack.renderer.config

rules.push({
    test: /\.(png|jpg|svg|jpeg|gif)$/i,
    use: [
        {
            loader: 'file-loader',
            options: {
                name: 'img/[name].[ext]',
                publicPath: '../.'
            }
        },
    ],
});

I needed to set publicPath because in prod files are relative to your entryPoints.name (which is "main_window" by default with electron-forge) but the asset files are copied into render not render/main_window

In a random js/ts file:

import porcelain from './img/matcap-porcelain-white.jpg';

And my file is located in '/src/img/matcap-porcelain-white.jpg

This really did wonders. Thanks Mate !!

@anztrax
Copy link

anztrax commented Aug 20, 2021

This is awesome it worked wonders for me, the only issue I had was using a newer version of copy-webpack-plugin so I had to use a slightly different way to wrap it:

const copyPlugins = assets.map((asset) => {
  return new CopyWebpackPlugin({
    patterns: [{ from: path.resolve(__dirname, 'src', asset), to: asset }],
  });
});

it requires the patterns wrapping now and then it works this was very helpful

thanks bro this works for me 👍

@AlexPasharin
Copy link

Thanks, you are live saver!!!

@Daveiano
Copy link

Daveiano commented Feb 18, 2022

Thank you very much!

EDIT: While this works in development, it does not work in production? Am I missing something?

@sharkrice
Copy link

Thank you very much!

@smarthug
Copy link

Thank you! It works!!

@JamesGrom
Copy link

@bbudd - thank you so much for sharing this

@acequants
Copy link

@a358003542
Copy link

Here is my solution:

webpack.renderer.config.js

const rules = require('./webpack.rules');

const CopyPlugin = require("copy-webpack-plugin");
rules.push({
  test: /\.css$/,
  use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
});

module.exports = {
  // Put your normal webpack config below here
  module: {
    rules,
  },
  plugins: [
    new CopyPlugin({
      patterns: [
        { from: 'resources/images', to: 'main_window/static/images' },
      ],
    }),
  ]
};

use copy-webpack-plugin copy the image file to the .webpack/renderer and the main_window .

and use like this to reference the image file:

        let image_src = `../main_window/static/${LatexSettings[button_key]["button_image"]}`

notice the .. double dot symbole, when in the npm start devserver it useless, but can work out. and in the package mode, the double dot symbole is very critical.

@rodolfojnn
Copy link

Thank you very much!

EDIT: While this works in development, it does not work in production? Am I missing something?

In production, images are bundled within resources/app.asar.
Try referencing the image in your HTML like this:

<img src="../img/img_src.png" />

This should ensure it works both in development and in production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment