Asset Loading#

Load images, fonts, and data files from your project. The asset system handles path resolution automatically between Editor and builds.

Quick Start#

Place files in the assets/ folder inside your working directory, then load them by relative path:

~/                             ← Working directory
├── assets/
│   ├── logo.png
│   ├── fonts/
│   │   └── Inter.ttf
│   └── data/
│       └── config.json
├── index.tsx
└── esbuild.config.mjs
import { loadImage, loadFont, loadJson } from "onejs-unity/assets"

const logo = loadImage("logo.png")
const font = loadFont("fonts/Inter.ttf")
const config = loadJson("data/config.json")

All paths are relative to assets/. No absolute paths needed.

Functions#

FunctionReturnsDescription
loadImage(path)Texture2D \VectorImageLoad an image (PNG, JPG, SVG)
loadFont(path)FontLoad a font file (TTF, OTF)
loadFontDefinition(path)FontDefinitionLoad a font for UI Toolkit styling
loadText(path)stringLoad a text file
loadJson<T>(path)TLoad and parse a JSON file
loadBytes(path)Uint8ArrayLoad raw bytes
assetExists(path)booleanCheck if an asset exists
getAssetPath(path)stringGet the resolved full path
All load functions are synchronous and will throw if the file is not found. Paths are case-sensitive on most platforms.

Using with Components#

The <Image> component's src prop is the simplest way to display images:

import { Image } from "onejs-react"

// Loads from assets/hero.png
<Image src="hero.png" style={{ width: 200, height: 200 }} />

For background images and custom fonts, use the load functions directly:

import { loadImage, loadFontDefinition } from "onejs-unity/assets"

// Background image on any element
<View style={{ backgroundImage: loadImage("bg.png"), width: 300, height: 200 }} />

// Custom font
<Label style={{ unityFontDefinition: loadFontDefinition("fonts/Inter.ttf") }}>
    Custom font text
</Label>

Path Resolution#

In the Editor, assets load directly from the filesystem. In builds, they load from StreamingAssets.

ContextResolved path for logo.png
Editor{WorkingDir}/assets/logo.png
Build{StreamingAssets}/onejs/assets/logo.png
You don't need to handle this yourself — the runtime resolves paths automatically.

Builds#

Assets are copied to StreamingAssets automatically during Unity builds. The JSRunnerBuildProcessor handles this:

  1. Finds each JSRunner's working directory
  2. Copies {WorkingDir}/assets/ to Assets/StreamingAssets/onejs/assets/
  3. Skips .meta files
  4. Flushes the destination first to remove stale files
No esbuild plugin or manual setup is needed. Just put files in assets/ and they'll be available in builds.

Unity Resources#

For assets that Unity processes at import time, you can use Unity's built-in Resources folder instead. Place files in Assets/Resources/, and Unity will import them as native asset types (e.g., SVG → VectorImage, PNG → Texture2D).

import { Image } from "onejs-react"
import { VectorImage } from "UnityEngine/UIElements"

const icon = CS.UnityEngine.Resources.Load("icon", VectorImage)
<Image image={icon} style={{ width: 64, height: 64 }} />

The path passed to Resources.Load is relative to any Resources/ folder and omits the file extension.

assets/ vs Resources/#

assets/ folderResources/ folder
Location{WorkingDir}/assets/Assets/Resources/
LoadingloadImage(), loadFont(), etc.Resources.Load()
ProcessingRaw files, read at runtimeUnity-imported at build time
Best forImages, fonts, JSON, text dataAssets needing Unity import processing
Most of the time, the assets/ folder is all you need. Use Resources/ when you specifically need Unity's asset pipeline to process the file first.

Async Resource Loading#

For loading Unity resources without blocking, use the loadResourceAsync global:

// Load a TextAsset from Resources/
const textAsset = await loadResourceAsync("MyData/config", CS.UnityEngine.TextAsset)
console.log(textAsset.text)

// Load without specifying type (returns UnityEngine.Object)
const asset = await loadResourceAsync("MyData/config")

// Returns null for non-existent resources (no exception)
const missing = await loadResourceAsync("DoesNotExist")
// missing === null

loadResourceAsync is also available as a named import from onejs-unity/assets:

import { loadResourceAsync } from "onejs-unity/assets"

With React:

import { useState, useEffect } from "react"

function ConfigPanel() {
    const [config, setConfig] = useState(null)

    useEffect(() => {
        let cancelled = false
        async function load() {
            const asset = await loadResourceAsync("GameConfig", CS.UnityEngine.TextAsset)
            if (!cancelled && asset) {
                setConfig(JSON.parse(asset.text))
            }
        }
        load()
        return () => { cancelled = true }
    }, [])

    if (!config) return <Label text="Loading..." />
    return <Label text={`Difficulty: ${config.difficulty}`} />
}
loadImage / loadTextloadResourceAsync
Sourceassets/ folder (raw files)Unity Resources/ folder
Sync/AsyncSynchronousAsynchronous (returns Promise)
Asset processingRaw bytes/text at runtimeUnity-imported at build time
Not foundThrows errorReturns null

Package Assets#

npm packages can distribute assets using the @namespace/ convention:

my-ui-kit/
├── package.json
├── assets/
│   └── @my-ui-kit/
│       ├── icons/
│       │   └── check.png
│       └── backgrounds/
│           └── gradient.png
└── src/
    └── index.ts

Load them with the @ prefix:

const icon = loadImage("@my-ui-kit/icons/check.png")

The @namespace/ folder is detected automatically. In the Editor, assets resolve through node_modules. In builds, they're copied alongside project assets to StreamingAssets/onejs/assets/@my-ui-kit/.

To include package assets in the manifest for Editor resolution, add copyAssetsPlugin to your esbuild config:

import { copyAssetsPlugin } from "onejs-unity/esbuild"

const config = {
    plugins: [
        copyAssetsPlugin(),
    ],
}

This generates .onejs/assets-manifest.json which maps @namespace/ prefixes to their node_modules locations.