shadcn.io is an independent project built on shadcn/ui
Edit Profile opens Edit profile. A gradient cover has buttons to change or remove it, and Margaret Villard's photo overlaps the corner. The fields show Margaret, Villard, margaret-villard-69 with a check, a website prefixed https://, and a biography with characters left.
Use it to edit a profile without a settings page. The check does not look the name up. A longer editor fits a Sheet. In Svelte, Ark UI does not save the profile.
Installation
Anatomy

Usage
<Dialog><DialogTrigger>{#snippet asChild(props)}<Button {...props()} variant="outline">Edit Profile</Button>{/snippet}</DialogTrigger><DialogContent class="gap-0 p-0 sm:max-w-2xl"><DialogHeader class="px-6 py-4"><DialogTitle>Edit profile</DialogTitle></DialogHeader><div class="relative"><div class="h-40 bg-linear-to-r from-primary/30 via-primary/15 to-muted"></div><div class="absolute end-4 top-4 flex gap-2"><Button aria-label="Change cover image" pill size="icon-sm" variant="secondary"><ImageIcon aria-hidden="true" /></Button><Button aria-label="Remove cover image" pill size="icon-sm" variant="secondary"><XIcon aria-hidden="true" /></Button></div><div class="absolute start-6 -bottom-12"><Avatar class="size-24 ring-4 ring-background"><AvatarImage alt="Margaret Villard" src="https://github.com/shadcn.png" /><AvatarFallback>MV</AvatarFallback></Avatar><Button aria-label="Change profile photo" class="absolute end-0 bottom-0" pill size="icon-sm"><ImageIcon aria-hidden="true" /></Button></div></div><FieldGroup class="px-6 pt-16 pb-6"><div class="grid grid-cols-2 gap-4"><Field><FieldLabel>First name</FieldLabel><Input defaultValue="Margaret" name="first-name" type="text" /></Field><Field><FieldLabel>Last name</FieldLabel><Input defaultValue="Villard" name="last-name" type="text" /></Field></div><Field><FieldLabel>Username</FieldLabel><InputGroup><InputGroupInput name="username" oninput={handleUsernameChange} type="text" value={username} /><InputGroupAddon align="inline-end"><CheckIcon aria-hidden="true" class="text-success" /></InputGroupAddon></InputGroup></Field><Field><FieldLabel>Website</FieldLabel><InputGroup><InputGroupInput defaultValue="www.margaret.com" name="website" type="text" /><InputGroupAddon align="inline-start"><InputGroupText>https://</InputGroupText></InputGroupAddon></InputGroup></Field><Field><FieldLabel>Biography</FieldLabel><Textarea class="min-h-25 resize-none" maxlength={maxBioLength} name="bio" oninput={handleBioChange} value={bio} /><FieldDescription class="text-end">{maxBioLength - bio.length} characters left</FieldDescription></Field></FieldGroup><DialogFooter class="border-t px-6 py-4"><DialogClose>{#snippet asChild(props)}<Button {...props()} variant="outline">Cancel</Button>{/snippet}</DialogClose><Button>Save changes</Button></DialogFooter></DialogContent></Dialog>FAQ
How do the image buttons upload?
They're icon buttons with an aria-label and no handler. Have each open a hidden file input with accept="image/*", preview the file, and upload it when people save.
Does the check mean the username is available?
It's a static text-success check in an InputGroupAddon. Check availability as people type, with a short debounce, and swap the icon for a spinner or an error while it runs.
How does the character counter work?
The textarea is controlled with maxLength of 200, and a FieldDescription under it shows 200 minus the current length.
How is the https:// prefix built?
An InputGroup with the InputGroupInput and an InputGroupAddon holding InputGroupText. The addon comes after the input in the markup, so clicking it focuses the input, and align="inline-start" shows it first.
API Reference
Dialog
No extra props in this demo. The rest is on the Dialog page.
Button
Sets props, variant="outline", aria-label="Change cover image", pill, size="icon-sm" and variant="secondary". The rest is on the Button page.
Avatar
Sets alt="Margaret Villard" and src="https://github.com/shadcn.png". The rest is on the Avatar page.
Field
No extra props in this demo. The rest is on the Field page.
Input
Sets defaultValue="Margaret", name="first-name", type="text", defaultValue="Villard" and name="last-name". The rest is on the Input page.
Input Group
Sets name="username", oninput={handleUsernameChange}, type="text", value={username}, align="inline-end" and defaultValue="www.margaret.com". The rest is on the Input Group page.
Textarea
Sets maxlength={maxBioLength}, name="bio", oninput={handleBioChange} and value={bio}. The rest is on the Textarea page.





