Not affiliated with official shadcn/ui. Visit ui.shadcn.com for official docs.
Unlock this block—Get Pro at 60% offReact Dialog Block Share Permissions
Share permissions dialog with user search, role assignment dropdown, existing collaborators list, and access level management
Looking to implement shadcn/ui blocks?
Join our Discord community for help from other developers.
Collaborate with granular control. This React share permissions dialog provides comprehensive sharing controls with user search autocomplete, role-based access dropdown, existing collaborator management, and permission inheritance display. Built with shadcn/ui Dialog, Input, Select, Avatar, Button, Badge, and ScrollArea components using Tailwind CSS, team leads assign viewer, editor, or admin access with visual role indicators and one-click permission changes. User search, role selector, remove access option—perfect for document collaboration platforms, project management tools, file sharing apps, or any Next.js application where controlled sharing and permission management ensures data security.
"use client";import { useState } from "react";import { Share2, Search, X, Check, ChevronDown, UserPlus, Crown, Eye, Pencil,} from "lucide-react";import { Button } from "@/components/ui/button";import { Badge } from "@/components/ui/badge";import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/ui/dialog";import { Input } from "@/components/ui/input";import { Label } from "@/components/ui/label";import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";import { ScrollArea } from "@/components/ui/scroll-area";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select";import { cn } from "@/lib/utils";type Permission = "viewer" | "editor" | "admin";type Collaborator = { id: string; name: string; email: string; avatar?: string; permission: Permission; isOwner?: boolean;};type SearchResult = { id: string; name: string; email: string; avatar?: string;};const existingCollaborators: Collaborator[] = [ { id: "1", name: "Sarah Chen", email: "[email protected]", avatar: "", permission: "admin", isOwner: true, }, { id: "2", name: "Michael Park", email: "[email protected]", avatar: "", permission: "editor", }, { id: "3", name: "Emma Wilson", email: "[email protected]", avatar: "", permission: "viewer", },];const searchResults: SearchResult[] = [ { id: "4", name: "Alex Thompson", email: "[email protected]" }, { id: "5", name: "Jordan Lee", email: "[email protected]" }, { id: "6", name: "Taylor Martinez", email: "[email protected]" },];const permissionConfig: Record<Permission, { label: string; icon: React.ElementType; description: string }> = { viewer: { label: "Viewer", icon: Eye, description: "Can view only" }, editor: { label: "Editor", icon: Pencil, description: "Can view and edit" }, admin: { label: "Admin", icon: Crown, description: "Full access" },};export const title = "React Dialog Block Share Permissions";export default function DialogSharePermissions() { const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); const [collaborators, setCollaborators] = useState<Collaborator[]>(existingCollaborators); const [newUserPermission, setNewUserPermission] = useState<Permission>("viewer"); const [showResults, setShowResults] = useState(false); const [saved, setSaved] = useState(false); const filteredResults = searchResults.filter( (user) => !collaborators.find((c) => c.id === user.id) && (user.name.toLowerCase().includes(search.toLowerCase()) || user.email.toLowerCase().includes(search.toLowerCase())) ); const handleAddUser = (user: SearchResult) => { setCollaborators([ ...collaborators, { ...user, permission: newUserPermission }, ]); setSearch(""); setShowResults(false); }; const handleRemoveUser = (userId: string) => { setCollaborators(collaborators.filter((c) => c.id !== userId)); }; const handleChangePermission = (userId: string, permission: Permission) => { setCollaborators( collaborators.map((c) => c.id === userId ? { ...c, permission } : c ) ); }; const handleSave = () => { console.log("Saving collaborators:", collaborators); setSaved(true); }; const handleClose = () => { setOpen(false); setTimeout(() => { setSearch(""); setShowResults(false); setSaved(false); setCollaborators(existingCollaborators); setNewUserPermission("viewer"); }, 200); }; const getInitials = (name: string) => { return name .split(" ") .map((n) => n[0]) .join("") .toUpperCase(); }; return ( <Dialog open={open} onOpenChange={setOpen}> <div className="flex min-h-[350px] items-center justify-center"> <DialogTrigger asChild> <Button variant="outline"> <Share2 className="mr-2 h-4 w-4" /> Share & Permissions </Button> </DialogTrigger> </div> <DialogContent className="sm:max-w-md"> {saved ? ( <> <DialogHeader className="text-center"> <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 mb-4"> <Check className="h-6 w-6 text-primary" /> </div> <DialogTitle>Permissions Updated</DialogTitle> <DialogDescription> {collaborators.length} people have access to this item. </DialogDescription> </DialogHeader> <DialogFooter> <Button onClick={handleClose} className="w-full"> Done </Button> </DialogFooter> </> ) : ( <> <DialogHeader> <DialogTitle className="flex items-center gap-2"> <Share2 className="h-5 w-5" /> Share & Permissions </DialogTitle> <DialogDescription> Manage who has access to this item. </DialogDescription> </DialogHeader> <div className="space-y-4 py-4"> <div className="space-y-2"> <Label>Add people</Label> <div className="flex gap-2"> <div className="relative flex-1"> <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Input placeholder="Search by name or email..." value={search} onChange={(e) => { setSearch(e.target.value); setShowResults(e.target.value.length > 0); }} onFocus={() => search && setShowResults(true)} className="pl-9" /> {showResults && filteredResults.length > 0 && ( <div className="absolute top-full left-0 right-0 mt-1 rounded-md border bg-popover shadow-md z-10"> {filteredResults.map((user) => ( <button key={user.id} onClick={() => handleAddUser(user)} className="w-full flex items-center gap-3 px-3 py-2 hover:bg-muted transition-colors" > <Avatar className="h-8 w-8"> <AvatarImage src={user.avatar} /> <AvatarFallback className="text-xs"> {getInitials(user.name)} </AvatarFallback> </Avatar> <div className="text-left"> <p className="text-sm font-medium">{user.name}</p> <p className="text-xs text-muted-foreground"> {user.email} </p> </div> <UserPlus className="ml-auto h-4 w-4 text-muted-foreground" /> </button> ))} </div> )} </div> <Select value={newUserPermission} onValueChange={(value) => setNewUserPermission(value as Permission)} > <SelectTrigger className="w-[110px]"> <SelectValue /> </SelectTrigger> <SelectContent> {Object.entries(permissionConfig).map(([key, config]) => ( <SelectItem key={key} value={key}> <span className="flex items-center gap-2"> <config.icon className="h-3 w-3" /> {config.label} </span> </SelectItem> ))} </SelectContent> </Select> </div> </div> <div className="space-y-2"> <Label>People with access</Label> <ScrollArea className="h-[180px] rounded-md border"> <div className="divide-y"> {collaborators.map((collaborator) => { const config = permissionConfig[collaborator.permission]; return ( <div key={collaborator.id} className="flex items-center gap-3 px-3 py-2" > <Avatar className="h-8 w-8"> <AvatarImage src={collaborator.avatar} /> <AvatarFallback className="text-xs"> {getInitials(collaborator.name)} </AvatarFallback> </Avatar> <div className="flex-1 min-w-0"> <div className="flex items-center gap-2"> <p className="text-sm font-medium truncate"> {collaborator.name} </p> {collaborator.isOwner && ( <Badge variant="secondary" className="text-xs px-1.5 py-0"> Owner </Badge> )} </div> <p className="text-xs text-muted-foreground truncate"> {collaborator.email} </p> </div> {collaborator.isOwner ? ( <span className="text-xs text-muted-foreground flex items-center gap-1"> <Crown className="h-3 w-3" /> Owner </span> ) : ( <div className="flex items-center gap-1"> <Select value={collaborator.permission} onValueChange={(value) => handleChangePermission(collaborator.id, value as Permission) } > <SelectTrigger className="h-7 w-[90px] text-xs"> <SelectValue /> </SelectTrigger> <SelectContent> {Object.entries(permissionConfig).map(([key, cfg]) => ( <SelectItem key={key} value={key}> <span className="flex items-center gap-2"> <cfg.icon className="h-3 w-3" /> {cfg.label} </span> </SelectItem> ))} </SelectContent> </Select> <Button variant="ghost" size="sm" className="h-7 w-7 p-0" onClick={() => handleRemoveUser(collaborator.id)} > <X className="h-4 w-4" /> </Button> </div> )} </div> ); })} </div> </ScrollArea> </div> </div> <DialogFooter className="gap-2 sm:gap-0"> <Button variant="outline" onClick={handleClose}> Cancel </Button> <Button onClick={handleSave}> Save Changes </Button> </DialogFooter> </> )} </DialogContent> </Dialog> );}Installation
npx shadcn@latest add https://www.shadcn.io/registry/dialog-share-permissions.jsonnpx shadcn@latest add https://www.shadcn.io/registry/dialog-share-permissions.jsonpnpm dlx shadcn@latest add https://www.shadcn.io/registry/dialog-share-permissions.jsonbunx shadcn@latest add https://www.shadcn.io/registry/dialog-share-permissions.jsonRelated blocks you will also like
React Dialog Block Invite Members
Team member invitation
React Dialog Block Team Roles
Role assignment interface
React Dialog Block Share Link
Link sharing toggle
React Dialog Block Create Workspace
Workspace with permissions
React Dialog Block Contact Card
User contact display
React Dialog Block Destructive Warning
Remove access confirmation