Asset Loading#

Load images, fonts, and data files from your project. Path resolution between Editor and builds is handled automatically.

Quick Start#

Place files in assets/ inside your working directory, then load 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")

Functions#

FunctionReturnsDescription
loadImage(path)Texture2D | VectorImageLoad PNG, JPG, or SVG. Accepts relative or absolute paths
loadImageFromUrl(url)Promise<Texture2D>Load an image from a URL
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 JSON
loadBytes(path)Uint8ArrayLoad raw bytes
assetExists(path)booleanCheck if an asset exists
getAssetPath(path)stringGet the resolved full path
All file-based functions are synchronous and throw if not found. loadImageFromUrl is async. Paths are case-sensitive on most platforms.

Using with Components#

The <Image> component's src prop loads from the assets folder:

import { Image } from "onejs-react"

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

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

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

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

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

Absolute Paths and URLs#

For files outside the assets/ folder (user save directories, Steam Workshop cache, remote servers), use absolute paths or URLs.

Absolute Paths#

All load functions accept absolute file paths. Asset resolution is bypassed entirely:

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

const thumb = loadImage("/path/to/steam/workshop/thumbnail.png")

// Works with <Image> src prop too
<Image src="/path/to/user/saves/preview.png" style={{ width: 128, height: 128 }} />

// Works with backgroundImage
<View style={{ backgroundImage: loadImage("/path/to/bg.png"), width: 300, height: 200 }} />

URLs#

loadImageFromUrl loads remote images. Returns a Promise<Texture2D>:

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

const tex = await loadImageFromUrl("https://example.com/thumbnail.jpg")
<Image image={tex} style={{ width: 128, height: 128 }} />
Note: Textures from absolute paths or URLs are not cached. Cache the Texture2D yourself if loading repeatedly, and call Object.Destroy() when done to avoid memory leaks.

Path Resolution#

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

ContextResolved path for logo.png
Editor{WorkingDir}/assets/logo.png
Build{StreamingAssets}/onejs/assets/logo.png
This is automatic. Absolute paths bypass resolution entirely.

Builds#

Assets are copied to StreamingAssets automatically during Unity builds via JSRunnerBuildProcessor:

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

Unity Resources#

For assets that need Unity's import pipeline, use the Resources/ folder instead. Place files in Assets/Resources/ and Unity imports them as native types.

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 }} />

Path is relative to any Resources/ folder, without 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 assets/ is all you need.

Async Resource Loading#

Load Unity resources without blocking:

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

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

// Returns null if not found
const missing = await loadResourceAsync("DoesNotExist")

With 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 (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 with the @ prefix:

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

In the Editor, assets resolve through node_modules. In builds, they're copied to StreamingAssets/onejs/assets/@my-ui-kit/.

To generate the manifest for Editor resolution, add copyAssetsPlugin to your esbuild config:

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

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