Collect a caption & category per file — sent with the upload
Declare metaFields and CoreUpload renders an inline input per field on each
pending file row. Image files in the thumbnail grid get a ✎ edit button that opens a
modal editor instead (also available programmatically via uploader.openMetaEditor(taskId)). Values are stored on task.metadata.meta[id] and travel to the
server automatically: as a metadata form field (single upload), a metadata
object in the chunk-complete body (chunked), and an X-Upload-Meta base64-JSON header on
both. Fields marked required block that file’s upload until filled — try
uploading with Category empty. Matches the per-file metadata UX of Uppy, Bytescale, and Filestack.
What you should see: queued files get Caption and Category inputs (images in the thumbnail grid get a ✎ modal instead). Leaving the required Category empty blocks that file with a message.
Drag & drop files here, or paste from clipboard
Metadata changes appear here as you type...
Declarative (Tag Helper)
<core-upload asp-upload-url="/api/upload/upload"
asp-auto-upload="false"
asp-meta-fields='[
{"id":"caption","label":"Caption"},
{"id":"category","label":"Category","required":true}
]'></core-upload>
JavaScript API
const up = CoreUpload.create('#uploader', {
metaFields: [
{ id: 'caption', label: 'Caption' },
{ id: 'category', label: 'Category', required: true }
],
onMetaChange: (task, id, value) => console.log(id, '=', value)
});
// Read/set programmatically
up.setMeta(taskId, 'caption', 'Beach sunset');
up.getMeta(taskId); // { caption: 'Beach sunset', category: '...' }
What the server receives
- Single upload: a
metadataform field —{"caption":"...","category":"..."}. - Chunked upload: a
metadataobject inside the/chunk/completeJSON body. - Both: an
X-Upload-Metarequest header (base64-encoded JSON) for pipelines that key off headers. - Direct-to-cloud (S3/Azure/GCS/tus): bytes bypass your server — attach metadata at your signing endpoint instead.