Resources
This page covers generic resource usage in Doors for src and href.
Use it for:
- files on disk
- embedded or virtual filesystem content
- generated bytes or strings
- handler-backed links
- proxied URLs
Generic resources usually fall into two groups:
- default resources, served through private, user-scoped URLs
- cached resources, served from shared resource storage with hash-based URLs
For JavaScript resources, see JavaScript. For stylesheet resources, see Styles.
Start
If you already have a normal URL, use a normal attribute:
<img src="/assets/logo.png">
<img src="https://cdn.example.com/logo.png">
<a href="https://example.com/report.pdf">Open report</a>
Use a resource when the content belongs to your app and Doors should turn it into a browser URL:
<img src=(doors.ResourceLocalFS("./private/logo.png"))>
<a href=(doors.ResourceBytes(reportPDF)) name="report.pdf">
Download report
</a>
In practice:
- plain URL: write the URL directly
- app-owned content: use a resource
- managed resources are private by default
Syntax
You can attach a resource either as a direct attribute value or with modifier syntax.
Direct attribute value:
<img src=(doors.ResourceLocalFS("./private/chart.png"))>
<a href=(doors.ResourceBytes(reportPDF)) name="report.pdf">
Download
</a>
Modifier syntax:
<img (doors.ResourceLocalFS("./private/chart.png"))>
<a
(doors.ResourceBytes(reportPDF))
name="report.pdf">
Download
</a>
Modifier syntax assigns the value to src or href from the tag name.
Use modifier syntax with actual resource values such as
doors.ResourceBytes(...)ordoors.ResourceLocalFS(...). Raw[]bytevalues and handler functions are supported onsrc=andhref=values, but not as standalone modifiers.
Sources
File on disk
<img src=(doors.ResourceLocalFS("./private/chart.png"))>
Embedded or virtual filesystem
//go:embed assets
var assets embed.FS
<img src=(doors.ResourceFS(assets, "assets/chart.png"))>
Bytes already in memory
<a href=(doors.ResourceBytes(reportPDF)) name="report.pdf">
Download report
</a>
String content already in memory
<iframe src=(doors.ResourceString(htmlSnippet)) name="preview.html"></iframe>
Simple custom response
<img src=(doors.ResourceHandler(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
w.Write(chartSVG)
})) name="chart.svg">
Full hook with Door context
<a href=(doors.ResourceHook(func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool {
w.Header().Set("Content-Type", "application/pdf")
w.Write(reportPDF)
return false
})) name="report.pdf">
Download
</a>
If a hook returns true, the resource is served only once. That is useful for one-time downloads and other short-lived links.
To get a hook URL without an attribute, for an API, server-sent events, or a WebSocket, see Custom Endpoints.
Request body is not bounded by
ServerRequestBodyLimitfor resource hooks. Apply a safe limit yourself if you read the body in aResourceHandlerorResourceHookcallback.
Proxy
<iframe src=(doors.ResourceProxy("https://example.com/embed"))></iframe>
Plain URLs
For generic src and href, already-hosted URLs should be written as plain strings:
<img src="https://cdn.example.com/logo.png">
<a href="/downloads/report.pdf">Open report</a>
Shorthands
For plain src= and href= usage, resources support a few shorthand forms.
[]byte
[]byte is treated like doors.ResourceBytes(...):
<img src=(pngBytes)>
<a href=(pdfBytes) name="report.pdf">
Download
</a>
Simple handler
A plain handler is treated like doors.ResourceHandler(...):
<img src=(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
w.Write(chartSVG)
}) name="chart.svg">
Full hook
A full hook-style handler is treated like doors.ResourceHook(...):
<a href=(func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool {
w.Header().Set("Content-Type", "application/pdf")
w.Write(reportPDF)
return true
}) name="report.pdf">
Download once
</a>
Cache
Add cache when the resource should be cacheable on the client side instead of being downloaded again on each render:
<img
src=(doors.ResourceFS(assets, "assets/logo.png"))
cache>
<a
href=(reportPDF)
cache
type="application/pdf">
Open report
</a>
What cache changes:
- the browser gets a hash-based URL it can reuse
- the resource becomes publicly reachable by that URL
- Doors keeps the cached entry in RAM
Use it for stable public content such as:
- embedded files
- local files that should behave like public assets
- generated bytes or strings that are not user-specific
Do not use it for large files. Cached entries are stored in RAM.
cache only works with static resource inputs:
doors.ResourceFS(...)doors.ResourceLocalFS(...)doors.ResourceBytes(...)doors.ResourceString(...)- raw
[]byte
Handler, hook, proxy, and plain string inputs cannot use cache.
Without cache, managed resources stay private to the current Door instance and can get a different URL on another render.
Even when name is set, a cached resource URL still contains a unique identifier. That makes cache a better fit for views and reusable assets than for user-facing download links where you want the cleanest possible filename.
URL in code
doors.NewResource(ctx, resource, contentType) returns the same cached URL without an attribute, for code that loads the file itself:
<script data:styles=({
url, _ := doors.NewResource(ctx, doors.ResourceBytes(widgetCSS), "text/css")
return url
})>
const link = document.createElement("link")
link.rel = "stylesheet"
link.href = $data("styles")
document.head.append(link)
</script>
- set
contentTypefor JavaScript and CSS; empty lets the server detect it, which givestext/plainfor them - append a file name after a dot when needed:
url + ".widget.css" - the error comes only from reading the resource, such as a missing file
Portable
Add portable when the URL must work without the browser session cookie:
<a
href=(doors.ResourceHook(func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool {
w.Header().Set("Content-Type", "application/pdf")
w.Write(reportPDF)
return false
}))
portable
name="report.pdf"
download>
Download report
</a>
A private resource URL is tied to the browser session, so it fails when something other than the page fetches it: an Android WebView hands downloads to the system download manager, which sends no cookies; apps and in-app browsers open links outside the page's cookie jar. A portable URL carries its own access instead.
What portable changes:
- the URL is a bearer link: anyone holding it can load the resource
- the link works while the content that rendered it lives, and stops when that content is cleared, the page instance or session ends, or a one-shot hook returns
true - a request through it needs no cookie and creates no session; the handler still sees the page's session, the stripped
r.URL.Path, andDoors-Hook-Prefixas with any hook
portable works with private and nocache; on handler, hook, and proxy sources it needs no other attribute. portable with cache is an error.
Handler, hook, and proxy sources are always served through a hook, so private is accepted on them and changes nothing. They do not take cache or nocache.
Content Type
Use type when a managed generic resource should set the response content type:
<img
src=(doors.ResourceFS(assets, "assets/chart.svg"))
type="image/svg+xml">
That applies to managed generic src and href resources. It does not apply to handler-backed resources.
Notes
Use name when the generated URL should carry a readable file name:
<a
href=(doors.ResourceBytes(reportPDF))
name="report.pdf">
Download report
</a>
This is useful for browser filename hints, inspection, and extension-based content handling.
Resources can be attached to the usual src and href elements. In practice, the most common ones are:
aimgiframelinkscriptvideoaudiosource
For generic non-script, non-stylesheet usage:
- use a plain URL when the URL already exists
- use a resource when your Go code owns the content
- prefer modifier syntax when the resource is the main point of the element
- use
cacheonly for stable public content - use
portablefor download links fetched outside the page, such as Android WebView downloads - use
namewhen the generated URL should look like a real file
<script>, <style>, and <link rel="stylesheet"> have richer resource behavior than the generic flow described here.