Blox/
Settings Panel
A comprehensive settings interface with sidebar navigation, form inputs, toggles, and theme selection. Perfect for user preferences and application configuration.
Sidebar NavForm InputsTogglesTheme Selector
Installation
Terminal
TSXReactTailwind
npx @uiblox/cli add settings-panelPreview
Layout:
Settings
Profile
JD
Use Cases
User Preferences
Allow users to customize their profile, notification preferences, and display settings.
Admin Dashboards
Configure application-wide settings, integrations, and security options.
Account Management
Manage billing, subscription plans, and payment methods.
Privacy Controls
Let users control data sharing, visibility, and security settings.
Source Code
settings-panel.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257"use client"; import { useState } from "react"; import { cn } from "@/lib/utils"; interface SettingSection { id: string; title: string; icon: React.ReactNode; } const sections: SettingSection[] = [ { id: "profile", title: "Profile", icon: <UserIcon /> }, { id: "account", title: "Account", icon: <KeyIcon /> }, { id: "notifications", title: "Notifications", icon: <BellIcon /> }, { id: "appearance", title: "Appearance", icon: <PaletteIcon /> }, { id: "privacy", title: "Privacy", icon: <ShieldIcon /> }, { id: "billing", title: "Billing", icon: <CreditCardIcon /> }, ]; export function SettingsPanel() { const [activeSection, setActiveSection] = useState("profile"); const [settings, setSettings] = useState({ // Profile name: "John Doe", email: "john@example.com", bio: "Product designer based in San Francisco.", // Account twoFactor: true, sessionTimeout: "30", // Notifications emailNotifs: true, pushNotifs: true, marketingEmails: false, weeklyDigest: true, // Appearance theme: "system", fontSize: "medium", compactMode: false, // Privacy profilePublic: true, showActivity: true, allowIndexing: false, }); const updateSetting = (key: string, value: any) => { setSettings((prev) => ({ ...prev, [key]: value })); }; return ( <div className="max-w-5xl mx-auto bg-white dark:bg-slate-900 rounded-2xl border border-slate-200 dark:border-slate-800 overflow-hidden"> <div className="flex"> {/* Sidebar */} <div className="w-64 border-r border-slate-200 dark:border-slate-800 p-4"> <h2 className="text-lg font-semibold text-slate-900 dark:text-white px-3 mb-4">Settings</h2> <nav className="space-y-1"> {sections.map((section) => ( <button key={section.id} onClick={() => setActiveSection(section.id)} className={cn( "w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors", activeSection === section.id ? "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300" : "text-slate-600 hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800" )} > {section.icon} {section.title} </button> ))} </nav> </div> {/* Content */} <div className="flex-1 p-8"> {activeSection === "profile" && ( <div className="space-y-6"> <div> <h3 className="text-xl font-semibold text-slate-900 dark:text-white mb-1">Profile Settings</h3> <p className="text-slate-500">Manage your public profile information.</p> </div> <div className="flex items-center gap-6"> <div className="w-20 h-20 rounded-full bg-purple-600 flex items-center justify-center text-white text-2xl font-bold"> JD </div> <div className="space-y-2"> <button className="px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white text-sm font-medium rounded-lg"> Change Avatar </button> <p className="text-xs text-slate-500">JPG, PNG or GIF. Max 2MB.</p> </div> </div> <div className="grid gap-4"> <div> <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">Display Name</label> <input type="text" value={settings.name} onChange={(e) => updateSetting("name", e.target.value)} className="w-full px-4 py-2.5 border border-slate-200 dark:border-slate-700 rounded-xl bg-transparent focus:outline-none focus:ring-2 focus:ring-purple-500" /> </div> <div> <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">Email</label> <input type="email" value={settings.email} onChange={(e) => updateSetting("email", e.target.value)} className="w-full px-4 py-2.5 border border-slate-200 dark:border-slate-700 rounded-xl bg-transparent focus:outline-none focus:ring-2 focus:ring-purple-500" /> </div> <div> <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">Bio</label> <textarea value={settings.bio} onChange={(e) => updateSetting("bio", e.target.value)} rows={3} className="w-full px-4 py-2.5 border border-slate-200 dark:border-slate-700 rounded-xl bg-transparent focus:outline-none focus:ring-2 focus:ring-purple-500 resize-none" /> </div> </div> <div className="flex justify-end"> <button className="px-6 py-2.5 bg-purple-600 hover:bg-purple-700 text-white font-medium rounded-xl"> Save Changes </button> </div> </div> )} {activeSection === "notifications" && ( <div className="space-y-6"> <div> <h3 className="text-xl font-semibold text-slate-900 dark:text-white mb-1">Notification Preferences</h3> <p className="text-slate-500">Choose how you want to be notified.</p> </div> <div className="space-y-4"> <SettingToggle label="Email Notifications" description="Receive notifications via email" checked={settings.emailNotifs} onChange={(v) => updateSetting("emailNotifs", v)} /> <SettingToggle label="Push Notifications" description="Receive push notifications in your browser" checked={settings.pushNotifs} onChange={(v) => updateSetting("pushNotifs", v)} /> <SettingToggle label="Marketing Emails" description="Receive emails about new features and offers" checked={settings.marketingEmails} onChange={(v) => updateSetting("marketingEmails", v)} /> <SettingToggle label="Weekly Digest" description="Get a weekly summary of your activity" checked={settings.weeklyDigest} onChange={(v) => updateSetting("weeklyDigest", v)} /> </div> </div> )} {activeSection === "appearance" && ( <div className="space-y-6"> <div> <h3 className="text-xl font-semibold text-slate-900 dark:text-white mb-1">Appearance</h3> <p className="text-slate-500">Customize how the app looks.</p> </div> <div> <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">Theme</label> <div className="grid grid-cols-3 gap-3"> {["light", "dark", "system"].map((theme) => ( <button key={theme} onClick={() => updateSetting("theme", theme)} className={cn( "p-4 rounded-xl border-2 transition-colors text-center capitalize", settings.theme === theme ? "border-purple-500 bg-purple-50 dark:bg-purple-900/20" : "border-slate-200 dark:border-slate-700 hover:border-slate-300" )} > {theme} </button> ))} </div> </div> <div> <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">Font Size</label> <select value={settings.fontSize} onChange={(e) => updateSetting("fontSize", e.target.value)} className="w-full px-4 py-2.5 border border-slate-200 dark:border-slate-700 rounded-xl bg-transparent focus:outline-none focus:ring-2 focus:ring-purple-500" > <option value="small">Small</option> <option value="medium">Medium</option> <option value="large">Large</option> </select> </div> <SettingToggle label="Compact Mode" description="Reduce spacing and padding throughout the interface" checked={settings.compactMode} onChange={(v) => updateSetting("compactMode", v)} /> </div> )} </div> </div> </div> ); } function SettingToggle({ label, description, checked, onChange }: { label: string; description: string; checked: boolean; onChange: (value: boolean) => void; }) { return ( <div className="flex items-center justify-between p-4 bg-slate-50 dark:bg-slate-800/50 rounded-xl"> <div> <p className="font-medium text-slate-900 dark:text-white">{label}</p> <p className="text-sm text-slate-500">{description}</p> </div> <button onClick={() => onChange(!checked)} className={cn( "relative w-12 h-6 rounded-full transition-colors", checked ? "bg-purple-600" : "bg-slate-300 dark:bg-slate-600" )} > <div className={cn( "absolute top-1 w-4 h-4 bg-white rounded-full transition-transform", checked ? "translate-x-7" : "translate-x-1" )} /> </button> </div> ); } function UserIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>; } function KeyIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" /></svg>; } function BellIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" /></svg>; } function PaletteIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /></svg>; } function ShieldIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>; } function CreditCardIcon() { return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" /></svg>; }