shadcn.io is not affiliated with official shadcn/ui
Testimonial With Portrait Drop Zone Block for React
The portrait slot beside a customer quote is a working drop zone: drop or choose a photo and it fills the tile, with a button to remove it.
Installation
Overview
Show a quote with a face before you have the photo. A short header sits above a split band between the page's rails: a drop zone on one side, a large quote on the other, with a divider between them whose + mark lands on the band's top rule. The drop zone is a framed figure with + marks on its corners: the customer's initials in a circle inside two hairline rings, "Drop a portrait here" and "or press to choose a file". Drop an image on it, or press it to choose a file, and the photo fills the tile so you can judge how it sits beside the quote. A "Remove photo" button puts the initials back. Nothing is uploaded: the file is previewed in the browser and never sent anywhere. Beside the photo, the quote leads, the customer's name and role follow, and a Shine Button links to the full story (it's the block's only primary control). It's built from File Upload, Guides, Avatar, Button and Shine Button, with CSS entrances that stay still under reduced motion and your theme's tokens. For design reviews, page builders and template previews, where visitors or editors try their own portrait before committing to one.
FAQ
Does it upload the photo anywhere?
No. The block uses the browser's own file handling: the chosen image is shown through an object URL that exists only in the visitor's tab, and it is released again when the preview is removed or the page closes. To keep the photo, read the file from the onFileChange handler's acceptedFiles and send it to your storage there.
Which files does it accept, and what if someone drops the wrong one?
One image at a time (accept="image/*" and maxFiles={1}); dropping a second image replaces the first. A file that isn't an image is turned away (a photo already in the tile stays) and the status line under the tile says "That file isn’t an image. Choose a PNG, JPG or WebP photo." Add maxFileSize to the FileUpload to cap the size, and extend the message to match.
How do keyboard and screen reader users use it?
The whole tile is one focusable drop zone named by its text, "Drop a portrait here, or press to choose a file". Enter or Space opens the file picker, so dragging is never required. Choosing a photo, rejecting a file and removing the photo are each announced politely by the status line under the tile, and the status is empty on load. After "Remove photo", focus returns to the tile, since the button hides itself when there is no photo. The decorative rings and initials are hidden from assistive technology.
How is the preview drawn?
The chosen file is laid over the drop zone as an image that fills the tile, with pointer events switched off so another drop still lands on the zone underneath; dropping a new photo replaces the old one. The tile keeps its shape (4:5 from lg, wide on smaller screens) with or without a photo, so nothing on the page shifts. The focus ring sits on the tile's frame, so it shows even when a photo covers the zone.
How do I swap the placeholder for a fixed photo in production?
Delete the FileUpload and render an image in the same aspect-4/5 frame with className="size-full rounded-xl object-cover" and an alt if the picture carries information the caption doesn't. The quote, the caption and the link beside it don't change.
How does it fit a phone, and a right-to-left page?
Below the lg breakpoint the drop zone becomes a short, wide tile above the quote, with a hairline between them, so the quote is still on the first screen; nothing scrolls sideways at 320 px. In right-to-left languages the tile and quote swap sides and the divider moves with them, and the quote's quotation marks stay at its ends through bdi.
Related
- Pro
Testimonial With Portrait
Audio, video, photoOne customer story for a finance close tool in a reverse split: a framed illustrated portrait that a close checklist overlaps on the start, the quote and a Shine Button on the end, with a solid divider between.
- Pro
Resizable Split Testimonials
Quote gridsA featured quote and two supporting quotes for a checkout platform in a resizable split inside a framed figure: drag the divider, or focus it and use the arrow keys, to give either side more room, with a Shine Button in the header row.
- Pro
Video Testimonials Dialog Grid
Audio, video, photoFive customer-story videos on shared hairlines, one large lead beside four smaller; each poster is drawn from UI with a play pill, and opens a player dialog with the speaker and a timed transcript.
- Pro
Split Layout Image Hero
Product windowsA reverse split hero for an AI code assistant: an editor drawn from UI runs edge to edge to the start rail, with a file tree, code, a ghost suggestion and an assistant panel; a divider and the copy sit at the end.
- Pro
Testimonial Form
Collect a reviewA form for writing your own testimonial about Quill, in a split page column: name, role, star rating, a quote with a character counter and a permission switch on one side, a live preview card in a card on the other, and a Shine Button to send.
- Pro
With Rating Testimonials
Collect a reviewOne large customer quote over a closing band you can rate in: choose stars and the average and the rating count change on the spot, with a note that nothing is saved.