Asset Handling
NOTE
The asset handling feature in this guide applies to the main process. For static asset handling of the renderer process, refer to Vite's Static Asset Handling guide.
Public Directory
The public directory defaults to <root>/resources
, dedicated to the main process and preload scripts. It can be configured via the main.build.publicDir
and preload.build.publicDir
option.
If you have assets such as icons
, executables
, wasm files
, etc., you can put them in this directory.
Note that:
- All assets in public directory are not copied to output directory. So when packaging the app, the public directory should be packaged together.
- It is recommended to use the
?asset
suffix to import public assets.
Warning
The public asset handling in renderers is different from the main process and preload scripts.
- By default, the working directory of renderers are located in
src/renderer
, so the public directory needs to be created in this directory. The default public directory is namedpublic
, which can also be specified byrenderer.build.publicDir
. - The renderer public asset will be copied to output directory.
Type Definitions
If you are a TypeScript user, make sure to add a *.d.ts
declaration file to get type checks and intellisense:
/// <reference types="electron-vite/node" />
/// <reference types="electron-vite/node" />
Also, you can add electron-vite/node
to compilerOptions.types
of your tsconfig
:
{
"compilerOptions": {
"types": ["electron-vite/node"]
}
}
{
"compilerOptions": {
"types": ["electron-vite/node"]
}
}
This will provide type definitions for asset imports (e.g. importing an *?asset
file).
Importing Asset as File Path
In main process, assets can be imported as file path using the ?asset
suffix:
import { Tray, nativeImage } from 'electron'
import appIcon from '../../build/icon.ico?asset'
let tray = new Tray(nativeImage.createFromPath(appIcon))
import { Tray, nativeImage } from 'electron'
import appIcon from '../../build/icon.ico?asset'
let tray = new Tray(nativeImage.createFromPath(appIcon))
In this example, appIcon
will be resolved to:
const path = require("path");
const appIcon = path.join(__dirname, "./chunks/icon-4363016c.ico");
const path = require("path");
const appIcon = path.join(__dirname, "./chunks/icon-4363016c.ico");
And the build/icon.ico
will be copied to output dir (out/main/chunks/icon-4363016c.ico
).
If appIcon
place in public directory
:
import { Tray, nativeImage } from 'electron'
import appIcon from '../../resources/icon.ico?asset'
let tray = new Tray(nativeImage.createFromPath(appIcon))
import { Tray, nativeImage } from 'electron'
import appIcon from '../../resources/icon.ico?asset'
let tray = new Tray(nativeImage.createFromPath(appIcon))
Then appIcon
will be resolved to:
const path = require("path");
const appIcon = path.join(__dirname, "../../resources/icon.ico");
const path = require("path");
const appIcon = path.join(__dirname, "../../resources/icon.ico");
And resources/icon.ico
will not be copied to output dir.
Importing Json File as File Path
When you import json file, it will be resolved to json object by Vite's json plugin. But sometimes we want to import it as file path, in this case we can use the ?commonjs-external&asset
suffix to import.
import jsonFile from '../../resources/config.json?commonjs-external&asset'
import jsonFile from '../../resources/config.json?commonjs-external&asset'
app.asar.unpacked
File Path Problem
Due to limitations of ASAR, we cannot pack all files into ASAR archives. Sometimes, this creates a problem as the path to the file changes, but your path.join(__dirname, 'binary')
is not changed. To make it work you need to fix the path. Convert app.asar
in path to app.asar.unpacked
.
You can use the ?asset&asarUnpack
suffix to support this. For example:
import bin from '../../resources/hello.exe?asset&asarUnpack'
import bin from '../../resources/hello.exe?asset&asarUnpack'
Then bin
will be resolved to:
const path = require("path");
const bin = path.join(__dirname, "../../resources/hello.exe").replace("app.asar", "app.asar.unpacked");
const path = require("path");
const bin = path.join(__dirname, "../../resources/hello.exe").replace("app.asar", "app.asar.unpacked");
Importing Native Node Modules
There are two ways to use *.node
modules.
- import as node module
import addon from '../../resources/hello.node'
console.log(addon?.hello())
import addon from '../../resources/hello.node'
console.log(addon?.hello())
- import as file path
import addon from '../../resources/hello.node?asset'
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const native = require(addon)
console.log(native?.hello())
import addon from '../../resources/hello.node?asset'
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const native = require(addon)
console.log(native?.hello())
It is important to note that native node modules usually have to be imported depending on the platform
and arch
. If you can ensure that the node module is generated according to the platform and arch, there will be no problem using the first import method. Otherwise, you can use the second method to import the correct node module according to the platform and arch.
Importing WebAssembly
NOTE
In Vite, .wasm
files can be processed via the ?init
suffix, which supports browsers but not Node.js (Electron main process).
In main process, pre-compiled .wasm
files can be imported with ?loader
. The default export will be an initialization function that returns a Promise of the wasm instance:
import loadWasm from '../../resources/add.wasm?loader'
loadWasm().then((instance) => {
// Exported function live under instance.exports
const add = instance.exports.add as (a: number, b: number) => number
const sum = add(5, 6)
console.log(sum); // Outputs: 11
})
import loadWasm from '../../resources/add.wasm?loader'
loadWasm().then((instance) => {
// Exported function live under instance.exports
const add = instance.exports.add as (a: number, b: number) => number
const sum = add(5, 6)
console.log(sum); // Outputs: 11
})
The init function can also take the imports
object which is passed along to WebAssembly.instantiate
as its second argument:
loadWasm({
env: {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({
initial: 256,
maximum: 512
}),
table: new WebAssembly.Table({
initial: 0,
maximum: 0,
element: 'anyfunc'
})
}
}).then(() => {
/* ... */
})
loadWasm({
env: {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({
initial: 256,
maximum: 512
}),
table: new WebAssembly.Table({
initial: 0,
maximum: 0,
element: 'anyfunc'
})
}
}).then(() => {
/* ... */
})
In renderer, See Vite's WebAssembly feature for more details.