Component Library
EduNest ships a typed, reusable component library in src/components — an admin UI kit (components/admin) and a 30+ field form kit (components/form). Each component is shown individually below — interact with it, then copy its snippet.
Btn
Four variants (primary, secondary, danger, ghost), two sizes, and a loading state.
import { Btn } from "@/components/admin/ui";
<Btn>Primary</Btn>
<Btn variant="secondary">Secondary</Btn>
<Btn variant="danger">Danger</Btn>
<Btn variant="ghost">Ghost</Btn>
<Btn size="sm">Small</Btn>
<Btn loading>Saving…</Btn>Text Fields
The input family extends native input props — value is a string, onChange is the native change event.
InputField
Labelled single-line text input with optional icon, helper text and error state.
import { InputField } from "@/components/form";
<InputField label="Full name" value={v}
onChange={(e) => setV(e.target.value)} />EmailField
Email input with built-in format validation.
import { EmailField } from "@/components/form";
<EmailField label="Email" value={v}
onChange={(e) => setV(e.target.value)} />PasswordField
Password input with a show/hide reveal toggle.
import { PasswordField } from "@/components/form";
<PasswordField label="Password" value={v}
onChange={(e) => setV(e.target.value)} />UrlField
URL input that normalises and validates web addresses.
import { UrlField } from "@/components/form";
<UrlField label="Website" value={v}
onChange={(e) => setV(e.target.value)} />NumberField
Numeric input with min/max/step and stepper buttons.
import { NumberField } from "@/components/form";
<NumberField label="Seats" min={0} value={v}
onChange={(e) => setV(e.target.value)} />PhoneField
Phone input with a country-code selector.
import { PhoneField } from "@/components/form";
<PhoneField label="Phone" value={v}
onChange={(e) => setV(e.target.value)} />TextareaField
Multi-line text input with an optional character limit.
import { TextareaField } from "@/components/form";
<TextareaField label="Bio" rows={3} value={v}
onChange={(e) => setV(e.target.value)} />Selects
Dropdowns bound to an options array; onChange yields the selected value(s).
SelectField
Single-choice dropdown.
import { SelectField } from "@/components/form";
<SelectField label="Status" value={v}
onChange={(v) => setV(v ?? "")} options={opts} />SearchableSelectField
Single-choice dropdown with a type-to-filter search box.
import { SearchableSelectField } from "@/components/form";
<SearchableSelectField label="Level" value={v}
onChange={(v) => setV(v ?? "")} options={opts} />MultiSelectField
Multi-choice dropdown that returns an array of values.
import { MultiSelectField } from "@/components/form";
<MultiSelectField label="Tags" value={v}
onChange={setV} options={opts} />Toggles, Checkboxes & Radios
SwitchField
On/off toggle with a label and optional helper text.
Toggle this on or off.
import { SwitchField } from "@/components/form";
<SwitchField label="Active" checked={on} onChange={setOn} />CheckboxField
A single boolean checkbox (event-based onChange).
import { CheckboxField } from "@/components/form";
<CheckboxField checked={c}
onChange={(e) => setC(e.currentTarget.checked)} label="Featured" />CheckboxGroupField
A group of checkboxes returning the selected values as an array.
import { CheckboxGroupField } from "@/components/form";
<CheckboxGroupField label="Show on" value={v}
onChange={setV} options={opts} />RadioGroupField
A single-choice radio group.
import { RadioGroupField } from "@/components/form";
<RadioGroupField label="Level" value={v}
onChange={setV} options={opts} />Tags, Rating, Range & Color
TagField
Free-form tag entry rendered as removable chips.
import { TagField } from "@/components/form";
<TagField label="Tags" value={tags} onChange={setTags} />RatingField
Star rating selector.
import { RatingField } from "@/components/form";
<RatingField label="Rating" value={n} onChange={setN} />RangeField
Slider for a single value or a [min, max] range.
import { RangeField } from "@/components/form";
<RangeField label="Progress" min={0} max={100}
value={n} onChange={setN} />ColorField
Colour swatch + hex input.
import { ColorField } from "@/components/form";
<ColorField label="Brand colour" value={hex} onChange={setHex} />Date & Time
DatePickerField
Calendar date picker storing an ISO date string.
import { DatePickerField } from "@/components/form";
<DatePickerField label="Starts on" value={d}
onChange={(v) => setD(v ?? "")} />TimePickerField
Clock time picker.
import { TimePickerField } from "@/components/form";
<TimePickerField label="Start time" value={t} onChange={setT} />Rich Text & Markdown
EditorField
WYSIWYG rich-text editor that outputs HTML.
import { EditorField } from "@/components/form";
<EditorField label="Description" value={html} onChange={setHtml} />MarkdownEditor
Markdown editor with a live preview toggle.
import { MarkdownEditor } from "@/components/form";
<MarkdownEditor value={md} onChange={setMd} />File & Image Upload
Drag-and-drop uploaders that push to your configured storage (Cloudflare R2).
ImageUploadField
Single image uploader with preview.
Upload image
import { ImageUploadField } from "@/components/form";
<ImageUploadField label="Course image"
onChange={(file) => setFile(file)} />FileField
Single file uploader with an accept filter.
Click to upload or drag and drop
.pdf,.zip
import { FileField } from "@/components/form";
<FileField label="Resource" accept=".pdf,.zip"
onChange={(file) => setFile(file)} />Display
StatusBadge
Compact active/draft pill used across admin lists.
import { StatusBadge } from "@/components/admin/ui";
<StatusBadge active />
<StatusBadge active={false} />Card
The standard rounded surface used to group admin content.
Revenue
$12,480
Last 30 days
import { Card } from "@/components/admin/ui";
<Card className="p-5">…</Card>PageHeader
The title block every admin screen opens with (icon, title, subtitle, actions).
Add-ons
Connect integrations and activate features.
import { PageHeader, Btn } from "@/components/admin/ui";
import { Plug } from "lucide-react";
<PageHeader icon={Plug} title="Add-ons"
subtitle="Connect integrations and activate features."
actions={<Btn size="sm">New</Btn>} />Table
The admin data table (Table + Td) used for courses, orders and customers.
| Course | Status | Price |
|---|---|---|
| Algebra 101 | Active | $49 |
| Calculus Crash Course | Draft | $79 |
import { Table, Td } from "@/components/admin/ui";
<Table headers={["Course", "Status", "Price"]}>
<tr><Td>Algebra 101</Td><Td>Published</Td><Td>$49</Td></tr>
</Table>Overlays
Click each button to trigger the overlay.
Modal
Standard dialog with header, body and footer.
import Modal from "@/components/admin/Modal";
<Modal open={open} onClose={() => setOpen(false)}
title="Edit course">…</Modal>ConfirmModal
Confirmation dialog for destructive actions.
import { ConfirmModal } from "@/components/admin/ui";
<ConfirmModal open={open} title="Delete this course?"
onConfirm={…} onCancel={() => setOpen(false)} />Toast
Auto-dismissing notification (success / error / info / warning).
import { Toast } from "@/components/admin/ui";
<Toast message={msg} type="success" onClose={() => setMsg("")} />More in the kit
The form kit ships additional fields not shown live because they need real upload handlers, generics, or async data sources to be meaningful:
- AvatarUpload, MultiFileField — avatar / multi-file uploaders (push to R2).
- SearchableMultiSelectField, AutocompleteField — async / typeahead selects backed by an API.
- RepeaterField — repeatable row groups (generic
renderRow). - RadioField, HiddenField, FormField, SearchSelect — primitives composed by the fields above.
- Admin UI kit: MultiImageUpload, VariantImageUpload, VideoUpload, FileUpload, ArrayEditor.