Upload Adapter
The UploadAdapter contract shared by image, file, and video nodes — plus retry.
image, file, and video share one upload contract. It's a single method:
Prop
Type
interface UploadContext {
signal: AbortSignal;
}
interface UploadResult {
url: string;
}Wiring it in
The adapter is always a prop passed to your own NodeView — never hardcoded into the registry
component. See Media & AI Node Views for the full
ReactNodeViewRenderer wiring; the node view itself calls the core's runUpload/retryUpload
helpers, which track in-flight/failed uploads in a PendingUploadRegistry keyed by block id (not
position) so retry resends the original File without the user picking again.
Retry
retryUpload (re)starts from the last File passed to runUpload for that block id, or from an
explicit override — the same path covers "attach a file to an empty placeholder" when nothing
has been attempted yet. A completed upload dispatches with addToHistory: false, so it never
becomes a separate undo step.
Mapping the result onto the node
By default a successful upload stores result.url as the node's src. Pass toAttrs to the
image/file/video options to persist more of what your server returns — it runs on the first
success and on every retry, and its attrs are merged over the node's existing ones (so file's
name/size/mime, read from the picked File at insert, survive unless you override them):
interface StoredUpload extends UploadResult {
name: string;
size: number;
mime: string;
}
createBlockKit({
file: {
toAttrs: (result) => {
const { url, name, size, mime } = result as StoredUpload;
return { src: url, name, size, mime };
},
},
});Signed URLs and files behind auth
resolveSrc (on image, file, video, and embed) rewrites a stored URL for display only:
Image.configure({
resolveSrc: (src, node) => signUrl(src), // string or Promise<string>
});The document — getHTML(), markdown, JSON, Yjs — keeps the canonical stored src (url for
embed); persisting a short-lived signed URL would break every later reader. Only node views
apply it: the registry node views do, through
useResolvedSrc from @slash-editor/react, which you can call from your own node view too. An
async result renders nothing until it settles (the raw URL is never requested meanwhile), a
result for a since-changed src is discarded, and a thrown error or rejection falls back to the
stored URL.