Uploads
Quoin never performs a request. You hand it an upload function, and the image and file blocks call it with the chosen File and a category that names what the file is for (the core blocks use 'image' and 'file'). It resolves with an UploadResult.
import { BlockCanvas, type QuoinBlock, type UploadFn } from 'quoin-editor';
import 'quoin-editor/styles.css';
const upload: UploadFn = async (file, category) => {
let response: Response;
try {
response = await fetch(`/api/uploads?category=${encodeURIComponent(category)}`, {
method: 'POST',
body: file,
});
} catch {
throw new Error('Could not reach the server. Check your connection and try again.');
}
if (!response.ok) {
throw new Error(response.status === 413 ? 'That file is too large.' : 'The upload was refused.');
}
const stored = (await response.json()) as { key: string };
return { storageKey: stored.key, filename: file.name, size: file.size };
};
const resolveAssetUrl = (storageKey: string) => `/api/files/${encodeURIComponent(storageKey)}`;
const reportError = (error: Error) => console.error(error);
export function Editor({ blocks, onChange }: { blocks: QuoinBlock[]; onChange: (next: QuoinBlock[]) => void }) {
return (
<BlockCanvas
blocks={blocks}
onChange={onChange}
upload={upload}
resolveAssetUrl={resolveAssetUrl}
onError={reportError}
/>
);
}UploadResultis{ storageKey, filename, size, assetId? }.storageKeyis an opaque key your storage can find the file by. It is what Quoin stores in the document.resolveAssetUrlturns astorageKeyinto the address a picture or a file is drawn from. On the canvas it defaults to the key itself. The image and file blocks,toHTML,QuoinViewandQuoinDiffuse an address only when it is http, https, blob, data or relative, and draw the picture as missing, or the file with no link, otherwise.toMarkdownwrites the address your resolver returns as given, with no such check, andtoPlainTextwrites no address for a picture or a file, only its alt text or its name.- An
uploadthat resolves to anything without a stringstorageKeyhas failed. The image or file block shows "Upload failed." and writes nothing. A file dropped or pasted on the canvas is left out, andonErrorreceives anErrorwith that same message. - Reject with an
Errorwhosemessageis safe to show. It is rendered verbatim beside the upload button. A rejection that is not anError, or anErrorwith an empty message, shows a generic "Upload failed." instead. - Catch network failures and rethrow with your own wording. A raw
fetchrejection such as "Failed to fetch" would otherwise be shown to the user verbatim. - The upload control is disabled while an upload is in flight.
- With no
uploadfunction, upload fields render disabled, with a note that uploads are not configured. onErrorreceives errors Quoin cannot show inline, such as a dropped file that failed to upload. It defaults toconsole.error.
The image and file blocks say what they write once an upload resolves, and Custom blocks shows the same upload reached from a block of your own.