worker:fs

The worker:fs global attribute binds the content of an element to the worker as an env.FS asset. The worker reads the text or bytes of the element, with its type and headers, from env.FS[key].

Syntax

html
<script type=application/json worker:fs=data.json>{"ok":true}</script>
<script type=text/markdown o:=.md worker:fs>…</script>
<script type=text/markdown o:=pub.md worker:fs=priv.md>…</script>

Value

key

The asset key, read as env.FS[key]. Keys need no .. An empty or valueless worker:fs uses the o: value of the element; with neither, the key is the empty string.

Default type

The type is looked up from the text after the last . of the key, or from the whole key when it has no dot. worker:fs=json gets application/json.

Description

worker:fs alone publishes nothing: the asset exists only in env.FS. Add o: to serve it as well, under the same name or another. <script o:=pub.md worker:fs=priv.md type=text/markdown> creates /pub.md and env.FS["priv.md"].

env is the second argument of the handler, fetch(request, env, ctx), and holds { KV, FS }, so write (request, { FS }) => …. With no assets, env.FS is {}. Otherwise FS is a getter that is not enumerable, so Object.keys(env) does not list it and { ...env } drops it: read env.FS or destructure { FS } directly.

Each entry has this shape, in both environments:

FieldValue
headersAn array of [name, value] pairs from the h:* attributes of the element.
typeA string or undefined: o:type, else the type for the extension of the key, else the MIME type of a data: URL, else application/octet-stream for binary data. No charset is added.
bodyA string. For binary entries it is base64.
bodyEncoding'base64', on binary entries only.
bodyBytesThe decoded byte count, on binary entries only.

Bodies follow the rules of o:: the default body of the element, o:content and binary <img src=data:…> elements. Entries with the same key are joined with a line break, and the first element's type and headers win. A duplicate that involves a binary entry is an error.

Usage notes

Assets stay on the page

worker:fs does not take the element off the page. A <script type=application/json worker:fs=data.json> stays in the served HTML with the attribute stripped. Add edge:remove or edge:redact to keep the asset out of the served page. It remains in the deployed source either way; see Keeping secrets out.

At deploy

Deploy extracts worker:fs assets first, from the complete source, before it removes workers and before redaction. Assets inside a <template>, on elements with edge:redact or edge:remove, and on a <script type=worker> itself are all included. The playground reads worker:fs before cleanup too, but it does not look inside templates.

On RTEdge.net the assets are written as JSON into the deployed worker script, so they count toward its size, and changing an asset needs a redeploy. The same object serves every request of a worker instance, so treat env.FS as read-only. Without an edge worker, the assets are extracted but nothing reads them. Code: Size lists them as ⚙ worker:fs assets.

Freshness in the playground

The playground rebuilds env.FS only when the output reloads in full, so after an edit to a data block the asset is stale until Load (F5). Add reload to the script, or sync=reload to any element, to rebuild it on every edit.

The response getter

Experimental On the first access to env.FS, each entry gets a non-enumerable getter named response. Every read returns a new Response: the body is decoded from base64 for binary entries, and the headers are the h:* pairs, then content-type when type is defined, then content-length for binary entries. It exists in both environments. A text entry without type gets the Response default, text/plain;charset=UTF-8. Set o:type instead of h:content-type on assets that you return this way.

Note: The response getter may change.

Older deployments

A deployment built with a wrapper older than 2025-09-29 has no env.FS. Redeploy it to get the current wrapper.

Examples

Serving Markdown from a worker

html
<iframe src=:></iframe>
<script o:=.md worker:fs type=text/markdown reload>
# Hello from the worker
</script>
<script type=worker edge io:guest>
  import { marked } from '//cdn.jsdelivr.net/npm/marked@latest/+esm';
  export default {
    fetch: (request, { FS }) => new Response(marked.parse(FS['.md'].body), { headers: { 'content-type': 'text/html;charset=utf-8' } })
  };
</script>

The empty worker:fs takes its key, .md, from o:=.md, so the same text is a public file at /.md and an asset in env.FS. <iframe src=:> asks the worker for its root, and reload keeps the asset fresh while you edit.

An asset that only the worker can read

html
<script type=application/json worker:fs=data.json edge:remove>{ "ok": true }</script>
<script type=worker edge>
  export default {
    fetch: (request, { FS }) => new Response(FS['data.json'].body, { headers: { 'content-type': FS['data.json'].type } })
  }
</script>
<iframe src=:></iframe>

type is application/json, taken from the .json extension of the key. edge:remove keeps the asset out of the served page; the source still holds it, so keep the source private if the data must not be public.

Specifications

Not part of any specification. worker:fs is an RTCode.io and RTEdge.net feature.

It builds on these standards:

Specification
Cloudflare Workers runtime
Fetch Standard

Availability

FeaturePlaygroundRTEdge.netNotes
worker:fs assets in env.FSYesYesThe same entry shape.
worker:fs inside a <template>NoYesThe playground does not look inside templates.
RefreshOn full reloadsOn deployRTEdge.net embeds the assets in the worker script.
response getter ExperimentalYesYesRedeploy older deployments to get it.
Agent File Sync (Agent Folder Share)YesNoIn an export that holds the whole element, an agent's change to worker:fs is restored. Edit it in 📄.html. Chrome or Edge on desktop.

See also