question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Provide an example of serving static files

See original GitHub issue

Preflight Checklist

  • I have read the contribution documentation for this project.
  • I agree to follow the code of conduct that this project follows, as appropriate.
  • I have searched the issue tracker for a feature request that matches the one I want to file, without success.

Problem Description

I’m having trouble getting images to load in Electron consistently. I’m using Electron Forge with the webpack template https://www.electronforge.io/templates/webpack-template

My src directory looks like this:

├── images
│   └── black.png
├── index.css
├── index.html
├── index.js
├── main.js
└── renderer.js

My HTML code looks like this:

<img src="images/black.png">

I’m using copy-webpack-plugin to copy the images directory.

When running in development (npm run start) the root of the dev server is .webpack/renderer so the image loads. When running in production (npm run package) the HTML file is being opened from the file system so the image tag is trying to access .webpack/renderer/main_window/images which is the wrong location and it doesn’t load.

I’ve seen the recommendation to import the image into renderer.js but I have a few thousand images. Should I do it like this?

import './images/1.png';
import './images/2.png';
import './images/3.png';
// ...
import './images/1000.png';

Is there a way to programmatically import? Something like:

let imageMap = {};
for (let i of Iter.range(1, 1001)) {
  let image = import(`./images/${i}.png`);
  imageMap[i] = image;
}

Then how would I refer to it in HTML? Can it be done without DOM editing code?

I prefer not to import my images into my JavaScript file and run them through webpack loaders. I just want to reference static files from my HTML code in a way that works in both development and production.

I also have a 5MB JSON file that I need to access using fetch(). I tried to import this through a loader but the build process took more than 5 minutes and I killed it.

Proposed Solution

I can think of a few ways to solve this:

  1. The webpack config needs to know via some kind of environment variable or flag if it is running in development or production and change the copy-webpack-plugin’s “to” path.
  2. Change the development server to run so its root is .webpack/renderer/main_window
  3. In production run a server instead of loading from the file system

I was able to implement the last option by running a static Express server in production serving the renderer directory. I use absolute paths (/images/foo.png) for image tags and can access my static files.

webpack.renderer.config.js

const path = require('path');
const rules = require('./webpack.rules');
const CopyWebpackPlugin = require('copy-webpack-plugin');

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

module.exports = {
  module: {
    rules: [
      ...rules,
    ],
  },
  plugins: [
    ...copyPlugins,
  ]
};

main.js

import { app, BrowserWindow } from 'electron';
import path from 'path';
import express from 'express';

function isDebug() {
  return process.env.npm_lifecycle_event === 'start';
}

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
  app.quit();
}

const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 1200,
    height: 740,
  });

  if (isDebug()) {
    // Create the browser window.
    mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
    mainWindow.webContents.openDevTools();
  } else {
    const exApp = express();
    exApp.use(express.static(path.resolve(__dirname, '..', 'renderer')));
    const server = exApp.listen(0, () => {
      console.log(`port is ${server.address().port}`);
      mainWindow.loadURL(`http://localhost:${server.address().port}/main_window/`);
    });
  }
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

Additional Information

I’ve seen a solution expressed in these links but I could not get it to work in both development and production.

https://github.com/electron-userland/electron-forge/issues/1196

https://github.com/electron-userland/electron-forge/issues/941

Conclusion

Loading static files is a basic part of making web pages and should be a part of the project template.

Please implement some kind of recommendation or code sample in the docs for static file serving. This is not the first ticket talking about this. People are spending huge amounts of time on it. This is something that should be simple but I’ve spent hours trying to figure it out.

I know there is an example of importing a CSS file into a JavaScript file but that it a unusable approach when you have thousands of large files to the point that the webpack dev server cannot even boot.

There is not an explanation of why the development system runs from a server and the production one runs from a file other than I suppose that the dev serve gives reloading ability. This mismatch is the root of the problem everyone is having. Running a server in production is the most reasonable way to make these the same.

And I just today saw a new ticket marking this as a bug https://github.com/electron-userland/electron-forge/issues/1591.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:27
  • Comments:14 (1 by maintainers)

github_iconTop GitHub Comments

13reactions
nikitasnvcommented, May 13, 2020

There is a solution if you are not using a server:

webpack.render.config.js

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

module.exports = {
    //...
    plugins: [
        ...copyPlugins 
    ],
};

You need to register own file protocol to handle custom requests for static files. Do it in window creation handler.

main.js

import { app, BrowserWindow, session } from 'electron';
//...
session.defaultSession.protocol.registerFileProtocol('static', (request, callback) => {
        const fileUrl = request.url.replace('static://', '');
        const filePath = path.join(app.getAppPath(), '.webpack/renderer', fileUrl);
        callback(filePath);
    });

Then you can use it in html like this:

 <img alt="" src="static://static/img.png" />
4reactions
Alpha018commented, May 19, 2021

@nikitasnv I try use this to solve my problem with the static assets but when i use this with copy-webpack-plugin throw me a error.

webpack.render.config.js

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

module.exports = {
    //...
    plugins: [
        ...copyPlugins 
    ],
};

i replace this for

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

and this work for me.

Tested on: copy-webpack-plugin@6.4.1

Read more comments on GitHub >

github_iconTop Results From Across the Web

Serving static files in Express
To serve static files such as images, CSS files, and JavaScript files, use the express.static built-in middleware function in Express. ... The root...
Read more >
What exactly does 'serving static files' mean? - Stack Overflow
Static files are typically files such as scripts, CSS files, images, etc... that aren't server-generated, but must be sent to the browser when ......
Read more >
Serving Static Files - Matt Layman
Static files are files that don't change when your application is running. These files do a lot to improve your application, but they...
Read more >
ExpressJS - Serving static files - Tutorialspoint
ExpressJS - Serving static files, Static files are files that clients download as they are from the server. Create a new directory, public....
Read more >
Serving Static Resources in Node.js - TutorialsTeacher
In this section, you will learn how to serve static resources like images, css, JavaScript or other static files using Express.js and node-static...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found