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.mjsimport { 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#
| Function | Returns | Description |
|---|---|---|
loadImage(path) | Texture2D | VectorImage | Load PNG, JPG, or SVG. Accepts relative or absolute paths |
loadImageAsync(path) | Promise<Texture2D | VectorImage> | Async loadImage. Works on all platforms |
loadImageFromUrl(url) | Promise<Texture2D> | Load an image from a URL |
loadFont(path) | Font | Load a font file (TTF, OTF) |
loadFontDefinition(path) | FontDefinition | Load a font for UI Toolkit styling |
loadText(path) | string | Load a text file |
loadTextAsync(path) | Promise<string> | Async loadText. Works on all platforms |
loadJson<T>(path) | T | Load and parse JSON |
loadJsonAsync<T>(path) | Promise<T> | Async loadJson. Works on all platforms |
loadBytes(path) | Uint8Array | Load raw bytes |
assetExists(path) | boolean | Check if an asset exists |
getAssetPath(path) | string | Get the resolved full path |
The synchronous functions throw if the asset is not found. Paths are case-sensitive on most platforms.
Android & WebGL: in these builds, StreamingAssets lives inside the app package (Android) or on a web server (WebGL) and can't be read synchronously. The synchronous functions throw there, so use the *Async variants, which work on every platform (in the Editor and on desktop they resolve immediately).
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.
Prefer a bare relative name for anything you ship."logo.png"is the only form that resolves correctly everywhere: the Editor reads it from your working directory, a desktop build fromStreamingAssets, and Android and WebGL builds fetch it over their platform's URL. An absolute path names one machine's filesystem and is not portable, so keep it for genuinely external files that only exist at runtime.
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 }} />The <Image> src prop also takes a URL directly, which saves you the state and effect. It loads asynchronously and the image appears once it arrives:
<Image src="https://example.com/thumbnail.jpg" style={{ width: 128, height: 128 }} />Note: Textures from absolute paths or URLs are not cached. Cache theTexture2Dyourself if loading repeatedly, and callObject.Destroy()when done to avoid memory leaks.
If a remote image never appears, update your packages. The URL bypass landed inonejs-react0.1.44. Before it, a full URL insrcwas rewritten to{streamingAssets}/onejs/assets/https://..., which nothing could fetch, on every platform including the Editor and with no error logged. Separately, OneJS before 3.2.1 never settled any promise in a WebGL build, which stalledloadImageFromUrland thesrcprop's async load alike: see Building & Deployment.
Path Resolution#
In the Editor, assets load from the filesystem. In builds, from StreamingAssets.
| Context | Resolved path for logo.png |
|---|---|
| Editor | {WorkingDir}/assets/logo.png |
| Build | {StreamingAssets}/onejs/assets/logo.png |
This is automatic. Absolute paths bypass resolution entirely.
On Android, {StreamingAssets} is a URL inside the APK (jar:file://...), and on WebGL it's an http(s) URL. The <Image> src prop and the *Async functions load these through UnityWebRequest automatically.
Builds#
Assets are copied to StreamingAssets automatically during Unity builds via JSRunnerBuildProcessor:
- Finds each JSRunner's working directory
- Copies
{WorkingDir}/assets/toAssets/StreamingAssets/onejs/assets/ - Skips
.metafiles - 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/ folder | Resources/ folder | |
|---|---|---|
| Location | {WorkingDir}/assets/ | Assets/Resources/ |
| Loading | loadImage(), loadFont(), etc. | Resources.Load() |
| Processing | Raw files, read at runtime | Unity-imported at build time |
| Best for | Images, fonts, JSON, text data | Assets 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 / loadText | loadResourceAsync | |
|---|---|---|
| Source | assets/ folder (raw files) | Unity Resources/ folder |
| Sync/Async | Synchronous | Asynchronous (Promise) |
| Asset processing | Raw bytes/text at runtime | Unity-imported at build time |
| Not found | Throws error | Returns 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.tsLoad 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(),
],
}