Skip to main content

UploadInput

Collects files and images, by drag-and-drop or from a file dialog.

Small files travel inline as base64; from a configurable threshold up the component switches to a chunked provider (S3 multipart or resumable tus) and shows a progress bar. Which path is taken is decided by the schema, not by the caller.

The component itself is free. The chunked transport behind it is not: it ships in habarico-enterprise, and without that package a past-threshold file fails with an explanatory error rather than being inlined. Base64 uploads work with the free package alone.

Drag a file onto the area below to see the two upload paths in action — a small file is embedded inline, a large one shows a progress bar.

Example​

{/* Single image on the product itself */}
<UploadInput source="photo" showImagePreview maxFiles={1} resizeWidth={1600} cropAspectRatio={1} />

{/* Several images stored in a related model */}
<UploadInput source="gallery" showImagePreview maxFiles={5} accept={['.jpg', '.png']} />

Props​

NameTypeDefaultDescription
source (required)string—Name of the field in the form record
labelReactNodelabel from the schemaLabel above the drop area
helperTextReactNodehelpText from the schemaText under the drop area
readOnlybooleanfalseShows the current files without allowing changes
fullWidthbooleantrueStretches the drop area to its container
showImagePreviewbooleanfalseRenders a thumbnail per file; the schema wrapper turns it on for base64-image
acceptstring[]—Allowed file types — extensions (".pdf"), media types ("image/png") or wildcards ("image/*"); client-side filtering only. The drop area lists them as extensions (image/png → PNG; .jpg, .jpeg and image/jpeg all → JPG (JPEG)) and leaves out a wildcard or a media type without a known extension
maxFilesnumberunlimited1 makes this a single-value field; any other value (including unset) makes it multi-value
maxFileSizenumber—Max file size in bytes; rejected before the upload starts, and again server-side
resizeWidthnumber—Resize width in px applied before base64 encoding
cropAspectRationumber—Crop ratio applied before base64 encoding, e.g. 1 for square
uploadUploadTransportConfig—Chunked-upload configuration — see below
labelsUploadAreaLabelsra.upload_images.* translationsOverrides the drop-area strings
validateValidator | Validator[]from the schemaReact Admin validators

Schema options​

The schema layer maps the field's options and format onto the props above before rendering this component directly:

Schema keyProp
options.accept, or [".png", ".jpg", ".jpeg", ".bmp", ".gif"] for format: "base64-image"accept
options.maxFiles on an array field; 1 on a direct object fieldmaxFiles
options.maxSizemaxFileSize
options.image.resizeWidthresizeWidth
options.image.cropAspectRatiocropAspectRatio
options.uploadupload
format === "base64-image"showImagePreview

Single-file vs multi-file​

maxFilesModeValue sent
1single{filename, content} (base64) or {upload_key} (chunked)
anything else, including unsetmultian array of the above, one entry per file

A direct base64-image / base64-file field is always capped at one file; an array field passes its own options.maxFiles through, and an unset one means unlimited.

Chunked uploads​

options.upload is injected by the backend when a chunked provider is configured; you do not write it by hand. Rendering it needs habarico-enterprise — see File uploads.

KeyTypeDescription
provider's3-multipart' | 'tus'Presigned PUT/multipart to S3, or a resumable tus server
thresholdnumberFile size in bytes at which the chunked path takes over from base64
chunkSizenumberPart size in bytes
minChunkSizenumbers3-multipart only — S3's 5 MiB floor; decides single PUT vs. multipart
initiateUrlstrings3-multipart only — presign endpoint, already carrying ?model=…&field=…
endpointstringtus only — tus server URL

Once a chunked upload finishes, the field value is {upload_key}; the backend serializer moves the temporary object into its final location on save.

caution

resizeWidth and cropAspectRatio only apply to the base64 path. A file large enough to take the chunked path is uploaded as-is. To resize every image, keep threshold above the largest expected image or resize server-side.

tip

Only the tus provider resumes an interrupted upload, and only while the temporary object on the server is still alive. s3-multipart restarts from the beginning after a page reload.

Schema mapping​

type: "object" with format: "base64-image" or "base64-file", or an array whose items carry one of those formats:

{"name": "photo", "type": "object", "format": "base64-image",
"options": {"accept": [".jpg", ".png"], "maxSize": 5242880,
"image": {"resizeWidth": 1600}}}

A direct image field on the model:

from habarico.serializers import AdminImageFileSerializer, AdminModelSerializer


class ProductSerializer(AdminModelSerializer):
photo = AdminImageFileSerializer(required=False)

class Meta:
model = Product
fields = ["id", "name", "photo"]

Several images stored in a related model — many=True turns the input into multi-file mode:

class ProductSerializer(AdminModelSerializer):
# write_only: the files are stored in ProductImage, not on Product itself
gallery = AdminImageFileSerializer(many=True, required=False, write_only=True)

class Meta:
model = Product
fields = ["id", "name", "gallery"]

def create(self, validated_data):
images = validated_data.pop("gallery", [])
product = super().create(validated_data)
for position, file_obj in enumerate(images):
ProductImage.objects.create(product=product, file=file_obj, position=position)
return product

Chunked uploads are configured once for the project — see Chunked uploads.

See also​