Contact Management Wireframes with shadcn/ui
Status: Contact Interface Specifications
Framework: shadcn/ui + Radix UI + Tailwind CSS
Verified: Based on user research and competitor pain points
Reference: UI Architecture Guide
Executive Summary
Contact management should be invisible until you need it. These wireframes present a radically simplified approach to managing email contacts, addressing the core finding that users waste hours navigating complex CRM-like interfaces when they just need basic list management.
Design Philosophy
Based on Phase 1 findings of "CRM feature creep," our approach:
- Table view by default (not complex CRM cards)
- Inline editing (no popup modals)
- Smart bulk actions (common tasks prioritized)
- One-click import/export (no 10-step wizards)
- Visual tags and lists (color-coded recognition)
The Simplicity Advantage
Contact List View Wireframe
Evidence-Based Design Decisions
| Feature | Traditional CRM | Our Solution | User Benefit |
|---|---|---|---|
| Default View | Complex cards | Clean table | Scan 50 contacts at once |
| Contact Fields | 50+ custom fields | 8 essential fields | No overwhelm |
| Bulk Actions | Buried in menus | Persistent toolbar | One-click operations |
| Search | Advanced filters | Simple search + filters | Find anyone in seconds |
| Import/Export | Multi-step wizard | One-click process | 2-minute migration |
Page Layout Structure
Header Section (140px)
Components:
- Page title with total count
- Quick stats (subscribed/unsubscribed)
- Global navigation
Key Metrics Display:
Contacts
2,847 total contacts โข 2,103 subscribed โข 744 unsubscribed
Action Bar with shadcn/ui (40px)
// Action bar using shadcn/ui components
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Badge } from "@/components/ui/badge"
import { Search, Filter, Download, Upload, Plus, X } from "lucide-react"
export function ContactActionBar({ filters, onSearch, onFilter, onImport, onExport }) {
return (
<div className="flex items-center justify-between p-4 border-b">
{/* Left Side - Discovery */}
<div className="flex items-center gap-3">
{/* Search Box */}
<div className="relative w-[300px]">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search contacts..."
className="pl-8"
onChange={(e) => onSearch(e.target.value)}
/>
{filters.search && (
<Button
variant="ghost"
size="icon"
className="absolute right-1 top-1 h-7 w-7"
onClick={() => onSearch('')}
>
<X className="h-4 w-4" />
</Button>
)}
</div>
{/* Quick Filters */}
<div className="flex items-center gap-2">
<Button variant="outline" size="sm">
<Filter className="mr-2 h-4 w-4" />
Filters
{filters.count > 0 && (
<Badge variant="secondary" className="ml-2">
{filters.count}
</Badge>
)}
</Button>
<Select>
<SelectTrigger className="w-[150px]">
<SelectValue placeholder="All Lists" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Lists</SelectItem>
<SelectItem value="newsletter">Newsletter</SelectItem>
<SelectItem value="customers">Customers</SelectItem>
<SelectItem value="leads">Leads</SelectItem>
</SelectContent>
</Select>
<Select>
<SelectTrigger className="w-[150px]">
<SelectValue placeholder="All Tags" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Tags</SelectItem>
<SelectItem value="vip">VIP</SelectItem>
<SelectItem value="beta">Beta</SelectItem>
<SelectItem value="trial">Trial</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Right Side - Actions */}
<div className="flex items-center gap-2">
<Button variant="outline" onClick={onExport}>
<Download className="mr-2 h-4 w-4" />
Export
</Button>
<Button variant="outline" onClick={onImport}>
<Upload className="mr-2 h-4 w-4" />
Import
</Button>
<Button>
<Plus className="mr-2 h-4 w-4" />
Add Contact
</Button>
</div>
</div>
)
}
Bulk Actions Bar with shadcn/ui (50px)
// Bulk actions bar using shadcn/ui components
import { Alert, AlertDescription } from "@/components/ui/alert"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Tag, Mail, FolderOpen, Trash2 } from "lucide-react"
import { motion, AnimatePresence } from "framer-motion"
export function BulkActionsBar({ selectedCount, totalCount, onSelectAll, onAction }) {
return (
<AnimatePresence>
{selectedCount > 0 && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 50, opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2 }}
>
<Alert className="rounded-none border-x-0 bg-blue-50 dark:bg-blue-950">
<AlertDescription className="flex items-center justify-between">
<div className="flex items-center gap-4">
<Checkbox
checked={selectedCount === totalCount}
onCheckedChange={onSelectAll}
/>
<span className="font-medium">
{selectedCount} of {totalCount} contacts selected
</span>
{/* Bulk Actions */}
<div className="flex items-center gap-2 ml-4">
<Button
variant="outline"
size="sm"
onClick={() => onAction('tag')}
>
<Tag className="mr-2 h-4 w-4" />
Add Tag
</Button>
<Button
variant="outline"
size="sm"
onClick={() => onAction('email')}
>
<Mail className="mr-2 h-4 w-4" />
Send Email
</Button>
<Button
variant="outline"
size="sm"
onClick={() => onAction('move')}
>
<FolderOpen className="mr-2 h-4 w-4" />
Move to List
</Button>
{/* Delete with confirmation */}
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="text-destructive"
>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete {selectedCount} contacts.
This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => onAction('delete')}
className="bg-destructive text-destructive-foreground"
>
Delete Contacts
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => onAction('clear')}
>
Clear Selection
</Button>
</AlertDescription>
</Alert>
</motion.div>
)}
</AnimatePresence>
)
}
Contact Table with shadcn/ui (560px)
Column Structure Implementation:
// Contact table using shadcn/ui components
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { Checkbox } from "@/components/ui/checkbox"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { ArrowUpDown, MoreHorizontal, Mail, Edit2, Trash2 } from "lucide-react"
export function ContactTable({ contacts, onSort, selectedIds, onSelectionChange }) {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[50px]">
<Checkbox
checked={selectedIds.length === contacts.length}
onCheckedChange={onSelectionChange}
/>
</TableHead>
<TableHead className="w-[300px]">
<Button variant="ghost" onClick={() => onSort('email')}>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
</TableHead>
<TableHead className="w-[200px]">
<Button variant="ghost" onClick={() => onSort('name')}>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
</TableHead>
<TableHead className="w-[200px]">Lists</TableHead>
<TableHead className="w-[200px]">Tags</TableHead>
<TableHead className="w-[150px]">
<Button variant="ghost" onClick={() => onSort('created_at')}>
Created
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
</TableHead>
<TableHead className="w-[150px]">Status</TableHead>
<TableHead className="w-[130px]">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{contacts.map((contact) => (
<TableRow
key={contact.id}
className="hover:bg-muted/50"
>
<TableCell>
<Checkbox
checked={selectedIds.includes(contact.id)}
onCheckedChange={() => onSelectionChange(contact.id)}
/>
</TableCell>
<TableCell className="font-medium">
<div className="flex items-center gap-2">
<Avatar className="h-8 w-8">
<AvatarImage src={contact.avatar} />
<AvatarFallback>{contact.initials}</AvatarFallback>
</Avatar>
<span>{contact.email}</span>
</div>
</TableCell>
<TableCell>{contact.name}</TableCell>
<TableCell>
<div className="flex gap-1 flex-wrap">
{contact.lists.map((list) => (
<Badge key={list} variant="secondary">
{list}
</Badge>
))}
</div>
</TableCell>
<TableCell>
<div className="flex gap-1 flex-wrap">
{contact.tags.map((tag) => (
<Badge key={tag} variant="outline">
{tag}
</Badge>
))}
</div>
</TableCell>
<TableCell>{contact.created}</TableCell>
<TableCell>
<Badge
variant={contact.status === 'subscribed' ? 'default' : 'destructive'}
>
{contact.status}
</Badge>
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>
<Mail className="mr-2 h-4 w-4" />
Send Email
</DropdownMenuItem>
<DropdownMenuItem>
<Edit2 className="mr-2 h-4 w-4" />
Edit Contact
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive">
<Trash2 className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
Row Design:
- 60px height (comfortable clicking)
- Alternating white/gray backgrounds
- Hover state: light blue
- Selected state: blue outline
Mobile Contact Management
Responsive Adaptations
For Sarah Chen's mobile usage:
List View (375px)
โโโโโโโโโโโโโโโโโโโ
โ Contacts (2,847)โ
โโโโโโโโโโโโโโโโโโโค
โ [๐ Search...] โ
โโโโโโโโโโโโโโโโโโโค
โ โโโโโโโโโโโโโโโ โ
โ โ SC Sarah... โ โ
โ โ Newsletter โ โ
โ โ VIP โข Sub โ โ
โ โโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโ โ
โ โ MR Marcus.. โ โ
โ โ Newsletter โ โ
โ โ Beta โข Sub โ โ
โ โโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโค
โ [+] Add Contact โ
โโโโโโโโโโโโโโโโโโโ
Key Mobile Optimizations
- Card view replaces table
- Essential info only (email, lists, status)
- Swipe actions (edit, delete)
- Sticky search at top
- Floating add button
Tag and List Management
Visual Tag System
Based on Phase 2 research showing visual recognition beats text:
Color-Coded Tags:
๐ข Green: Positive (Customer, VIP, Active)
๐ต Blue: Informational (Newsletter, Updates)
๐ก Orange: Warning (At Risk, Inactive)
๐ฃ Purple: Special (Beta, Power User)
๐ด Red: Negative (Unsubscribed, Bounced)
List Organization
Default Lists (System):
โโโ All Contacts
โโโ Subscribed
โโโ Unsubscribed
โโโ Bounced
Custom Lists (User):
โโโ Newsletter
โโโ Customers
โโโ Leads
โโโ [+ Create List]
Search and Filter Patterns
Quick Search Behavior
User types: "sarah"
Searches in: email, first name, last name
Results: Instant filtering of table
Clear: X button appears when typing
Filter Combinations
Common filters (from user research):
- Status: Subscribed/Unsubscribed
- List Membership: In specific lists
- Tags: Has/doesn't have tags
- Date Range: Joined within X days
- Engagement: Opened/clicked recently
Smart Filter Suggestions
Recent Filters:
- "Subscribed + Newsletter + Joined last 30 days"
- "VIP tag + Customer list"
- "Unsubscribed in last 7 days"
[Save as Smart List]
Import/Export Flows
One-Click Export
Click Export โ
โโโ Preparing CSV...
โโโ Including: Email, Name, Lists, Tags, Status, Created
โโโ 2,847 contacts exported
โโโ Download: contacts-2025-07-28.csv
Drag-Drop Import
Drag CSV file โ
โโโ Reading file...
โโโ Found 500 contacts
โโโ Mapping fields (auto-detected)
โโโ Checking for duplicates
โโโ 450 new, 50 updates
โโโ [Import] [Cancel]
Import Field Mapping
Your CSV โ NudgeCampaign Fields
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Email Address โ Email (required)
First Name โ First Name
Last Name โ Last Name
Company โ [Skip]
Newsletter โ List: Newsletter
Customer Type โ Tag: [Create New]
Individual Contact View
Contact Detail Sidebar (Slide-in)
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Sarah Chen โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ (SC) sarah@example.com โ
โ Subscribed โข Customer โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ CONTACT INFO โ
โ Name: Sarah Chen โ
โ Email: sarah@example โ
โ Phone: [Add] โ
โ Company: [Add] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ LISTS & TAGS โ
โ Lists: Newsletter โ โ
โ Customers โ โ
โ Tags: VIP, Active โ
โ [+ Add Tag] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ACTIVITY โ
โ Joined: May 15, 2025 โ
โ Emails Sent: 12 โ
โ Opens: 8 (66%) โ
โ Clicks: 3 (25%) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ [Edit] [Send Email] โ
โ [Unsubscribe] [Delete]โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
Performance Optimizations
Based on Phase 8 Architecture
Progressive Loading
Initial Load (0-500ms)
- First 50 contacts
- Basic table structure
- Search/filter UI
Background Load (500-2000ms)
- Remaining contacts
- Tag/list metadata
- Activity summaries
Virtual Scrolling
// Only render visible rows
const visibleRows = 20;
const rowHeight = 60;
const totalHeight = contactCount * rowHeight;
// Render only contacts in viewport
Bulk Operation Optimization
- Process in batches of 100
- Show progress indicator
- Allow cancel mid-operation
- Queue operations for reliability
Validation Against Success Metrics
Phase 1-2 Requirements
- Simple interface: Table view dominates
- Fast operations: Bulk actions prominent
- Easy migration: One-click import/export
- Mobile ready: Responsive card view
Phase 3-4 Technical
- Fast loading: Virtual scroll ready
- Scalable: Handles 100k+ contacts
- Real-time: Live search results
- Accessible: Keyboard navigation
Phase 5-7 Business
- Growth-friendly: Easy list building
- Segmentation: Tags and lists visible
- Integration ready: API-friendly structure
- GDPR compliant: Export/delete tools
๐ฎ Progressive Enhancement
MVP Features
- Basic table view
- Search by email/name
- Add/edit/delete contacts
- Simple import/export
- Lists and tags
Phase 2 Additions
- Advanced filters
- Custom fields (limited)
- Bulk email sending
- Activity timeline
- Duplicate management
Phase 3 Enhancements
- Smart lists (auto-updating)
- Engagement scoring
- Predictive segmentation
- API access
- Zapier integration
Implementation Notes
Component Structure
ContactManagement/
โโโ ContactTable/
โ โโโ TableHeader
โ โโโ TableRow
โ โโโ BulkActions
โ โโโ Pagination
โโโ ContactFilters/
โ โโโ SearchBox
โ โโโ FilterDropdowns
โ โโโ SavedFilters
โโโ ContactActions/
โ โโโ ImportModal
โ โโโ ExportButton
โ โโโ AddContactForm
โโโ ContactDetail/
โโโ ContactInfo
โโโ ActivityFeed
โโโ QuickActions
State Management
- Contact list array
- Selected contacts Set
- Active filters object
- Sort configuration
- Pagination state
Performance Checklist
- Implement virtual scrolling for large lists
- Debounce search input (300ms)
- Lazy load contact details
- Cache filter results
- Optimize bulk operations
Contact management wireframes designed to make list management invisible until needed, then powerful when required.