Guide

Onchfs

Choose a client-side service worker or a self-hosted proxy to resolve onchfs:// artwork.

Choose a resolver

Resolve onchfs:// URLs in one of two ways: run the worker in the browser, or host an HTTP proxy endpoint for your app and server-side tools. Choose the one that fits your deployment.

tsx
// Client-side browser resolution
const clientConfig = {
resolver: {
onchfs: { mode: "service-worker" },
},
}
// Your own HTTP endpoint
const serverConfig = {
resolver: {
onchfs: { mode: "proxy", baseUrl: "https://onchfs.example.com" },
},
}

1. Use the client-side worker

The standard Artwork component can resolve and execute onchfs:// artwork directly in the browser, without an fxhash endpoint or hosted backend.

Place the worker and its browser bundle in your public root. They must remain next to each other.

bash
pnpm add @whitehash/onchfs-sw
cp node_modules/@whitehash/onchfs-sw/dist/worker.js public/onchfs-sw.js
cp node_modules/@whitehash/onchfs-sw/dist/onchfs.global.js public/onchfs.global.js

2. Register the worker once

Mount the registration component beside your provider. After activation, every Artwork can resolve onchfs:// with no component changes.

tsx
"use client"
import { useEffect } from "react"
import { registerOnchfsWorker } from "@whitehash/onchfs-sw"
export function OnchfsRegistration() {
useEffect(() => {
void registerOnchfsWorker().catch(console.error)
}, [])
return null
}
// Mount <OnchfsRegistration /> once beside your provider.

3. Render normally

tsx
const { token, loading } = useToken({
chain: "eip155:1",
contract: "0xBb47F0ED4A7E3BffcA75660dFa3B053FB7FcE78E",
tokenId: "2953",
})
if (!loading && token) {
return <Artwork.Root token={token}></Artwork.Root>
}

Render an onchfs artwork in the browser

Genomes #2953

Genomes #2953

This example shows the composed Artwork component: an IPFS preview loads first, then Run onchfs live replaces it with the executable artwork. Its HTML, scripts, and assets are read from Ethereum through the same-origin worker.

Network
Ethereum
Collection
0xBb47…E78E
Token ID
2953
Generator root
046f4712…77f61

/.whitehash/onchfs/eip155-1/046f4712c2aaa344f82f1ef8ffed2ab8c9714819228e29c6a28cf67b14377f61/?fxhash=0x4d47331fb7ef118d98ff2c313fe79d2a6870a62ad078f625623d1122989b545e&fxiteration=2953&fxminter=0xb29DDe74b1ba90f3b21F12bA7ae7583976562EDD

How onchfs reaches the iframe

The service worker translates a browser-unreadable onchfs:// URI into a same-origin response. The artwork bytes remain content-addressed and chain-native all the way to the iframe.

The same Artwork API now covers both IPFS and onchfs content.

Use a self-hosted proxy instead

Choose proxy mode when service workers are unavailable or your application needs ordinary HTTP responses. Host the endpoint yourself and point baseUrl at it. Immutable generator bytes receive long-lived cache headers; query parameters still reach the runtime without duplicating those bytes.

bash
# From the onchfs-proxy app directory
pnpm install
PORT=3939 pnpm start

The same Hono app can run inside a Next.js route handler. Mount it under the path you will use as baseUrl:

tsx
import { Hono } from "hono"
import { createApp } from "@whitehash/onchfs-proxy"
const routes = new Hono()
routes.route("/api/onchfs", createApp())
export const runtime = "nodejs"
export const GET = (request: Request) => routes.fetch(request)
export const HEAD = GET
tsx
const config = {
resolver: {
onchfs: { mode: "proxy", baseUrl: "https://example.com/api/onchfs" },
},
}
bash
ONCHFS_ETH_RPCS=https://ethereum-rpc.example
ONCHFS_BASE_RPCS=https://base-rpc.example