Skip to content
Framework: Vite + React + TypeScriptDifficulty: intermediate

Admin panel

A CRUD members table with search, sortable columns, pagination, and validated create/edit forms.

An admin layout with a sidebar and a members resource: searchable, sortable, paginated table; create and edit dialogs with field-level validation and ARIA error wiring; activate/deactivate toggles and delete with confirmation. The data layer in src/api.ts mirrors the HTTP calls you would make (list, create, update, delete) so moving to a real API is a drop-in change.

  • react
  • admin
  • crud
  • table
  • forms
  • tailwind

This preview is the template's actual file tree and source code — exactly what is copied into a new project. Codexa does not publish screenshots or mock-ups of templates; run the project to see it rendered.

Files12 files

src/MemberForm.tsx64 lines
import { useState, type FormEvent } from "react";
import { validateMember, type Member, type Role } from "./api";

type Values = Omit<Member, "id" | "createdAt">;

export function MemberForm({ initial, onSave, onCancel }: { initial: Member | null; onSave: (v: Values) => void; onCancel: () => void }) {
  const [values, setValues] = useState<Values>(initial ?? { name: "", email: "", role: "viewer", active: true });
  const [errors, setErrors] = useState<Partial<Record<"name" | "email", string>>>({});

  function submit(e: FormEvent) {
    e.preventDefault();
    const errs = validateMember(values);
    setErrors(errs);
    if (Object.keys(errs).length === 0) onSave({ ...values, name: values.name.trim() });
  }

  return (
    <div className="fixed inset-0 z-20 flex items-end justify-center bg-black/40 p-4 sm:items-center" role="dialog" aria-modal="true" aria-labelledby="member-form-title">
      <form onSubmit={submit} className="w-full max-w-md space-y-3 rounded-xl bg-white p-5 shadow-xl" noValidate>
        <h2 id="member-form-title" className="text-lg font-semibold">
          {initial ? "Edit member" : "Add member"}
        </h2>
        <label className="block text-sm">
          <span>Name</span>
          <input value={values.name} onChange={(e) => setValues({ ...values, name: e.target.value })} aria-invalid={Boolean(errors.name)} aria-describedby={errors.name ? "name-error" : undefined} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" autoFocus />
          {errors.name ? (
            <span id="name-error" className="text-xs text-red-600">
              {errors.name}
            </span>
          ) : null}
        </label>
        <label className="block text-sm">
          <span>Email</span>
          <input type="email" value={values.email} onChange={(e) => setValues({ ...values, email: e.target.value })} aria-invalid={Boolean(errors.email)} aria-describedby={errors.email ? "email-error" : undefined} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
          {errors.email ? (
            <span id="email-error" className="text-xs text-red-600">
              {errors.email}
            </span>
          ) : null}
        </label>
        <label className="block text-sm">
          <span>Role</span>
          <select value={values.role} onChange={(e) => setValues({ ...values, role: e.target.value as Role })} className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2">
            <option value="admin">Admin</option>
            <option value="editor">Editor</option>
            <option value="viewer">Viewer</option>
          </select>
        </label>
        <label className="flex items-center gap-2 text-sm">
          <input type="checkbox" checked={values.active} onChange={(e) => setValues({ ...values, active: e.target.checked })} />
          Active
        </label>
        <div className="flex justify-end gap-2 pt-2">
          <button type="button" onClick={onCancel} className="rounded-md border border-slate-300 px-4 py-2 text-sm">
            Cancel
          </button>
          <button type="submit" className="rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white">
            Save
          </button>
        </div>
      </form>
    </div>
  );
}

Dependencies

Documented from the template's package.json. The runtime has a prebuilt dependency set for Vite + React + TypeScript, so installs are fast.

PackageVersion
react^19.1.0
react-dom^19.1.0
vite^6.3.0
@vitejs/plugin-react^4.4.0
tailwindcss^4.1.0
@tailwindcss/vite^4.1.0
typescript^5.8.0

How it runs

Framework
Vite + React + TypeScript
Dev command
npm run dev
Build command
npm run build
Output directory
dist
Port
5173
Opens at
/
Difficulty
intermediate

Projects run in an isolated sandbox with a live preview. Read the projects documentation for details on settings, snapshots and deployment.

Need the raw data? GET /api/v1/templates/vite-react-admin returns this template with its files.

Build on Admin panel

Create a free account, start from this template, and ask the AI agent to turn it into your app.