Skip to content

Instantly share code, notes, and snippets.

@bbudd
Created June 13, 2019 12:30
Show Gist options
  • Star 42 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>
@sanathks
Copy link

sanathks commented Jun 18, 2019

Thanks for sharing 👍

@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.

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