Simplify your workflow with our Next.js table-building tool powered by the ShadCN design library. Quickly scaffold essential features like search, pagination, sorting, create, edit, and delete. With seamless integration of ShadCN Form Builder , you can customize forms effortlessly and have a fully functional table ready in no time.
Whether you're a beginner or an experienced developer, our tool accelerates development while keeping it flexible and intuitive. Get started today and transform the way you build tables!
Name | Age | Gender | Email |
---|---|---|---|
Jane Doe | 30 | female | jane@test.com |
Alice Smith | 28 | female | alice@test.com |
Bob Johnson | 35 | male | bob@test.com |
Charlie Brown | 22 | male | charlie@test.com |
Diana Prince | 29 | female | diana@test.com |
The code will be look like this initially.
"use client"
import {
useState
} from "react"
import {
toast
} from "sonner"
import {
useForm
} from "react-hook-form"
import {
zodResolver
} from "@hookform/resolvers/zod"
import * as z from "zod"
import {
cn
} from "@/lib/utils"
import {
Button
} from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import {
Input
} from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from "@/components/ui/select"
const formSchema = z.object({
name_4603829743: z.string(),
name_0878515932: z.number(),
name_0706064476: z.string().optional(),
name_6646786819: z.string()
});
export default function MyForm() {
const form = useForm < z.infer < typeof formSchema >> ({
resolver: zodResolver(formSchema),
})
function onSubmit(values: z.infer < typeof formSchema > ) {
try {
console.log(values);
toast(
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(values, null, 2)}</code>
</pre>
);
} catch (error) {
console.error("Form submission error", error);
toast.error("Failed to submit the form. Please try again.");
}
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8 max-w-3xl mx-auto py-10">
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="name_4603829743"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="shadcn"
type="text"
{...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="name_0878515932"
render={({ field }) => (
<FormItem>
<FormLabel>Age</FormLabel>
<FormControl>
<Input
placeholder="shadcn"
type="number"
{...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="name_0706064476"
render={({ field }) => (
<FormItem>
<FormLabel>Gender</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a verified email to display" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="m@example.com">m@example.com</SelectItem>
<SelectItem value="m@google.com">m@google.com</SelectItem>
<SelectItem value="m@support.com">m@support.com</SelectItem>
</SelectContent>
</Select>
<FormDescription>You can manage email addresses in your email settings.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="name_6646786819"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
placeholder="shadcn"
type="email"
{...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
Ensure that the import paths align with your project's directory structure and naming conventions. Additionally, verify that all required packages are installed. Once these steps are completed, proceed to update the `form.tsx` file as outlined below.
// Update import paths
import { MyFormData } from "@types/table";
...
// update formSchema as per requirement add id as optional
const formSchema = z.object({
id: z.string().optional(),
name_4603829743: z.string().min(2, { message: "Name must be at least 2 characters" }),
name_0878515932: z.coerce.number().min(0, { message: "Age must be a positive number" }),
name_0706064476: z.string().optional(),
name_6646786819: z.string().email({ message: "Invalid email address" }),
});
// Add an interface for user form props
interface MyFormProps {
onSubmit: (data: MyFormData) => void;
initialData?: MyFormData | null;
}
// Define the MyForm component with enhanced functionality
export function MyForm({ onSubmit, initialData }: MyFormProps) {
...
// Set default values for the form
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: initialData || {
name_4603829743: "",
name_0878515932: 0,
name_0706064476: "",
name_6646786819: "",
},
})
//Add onSubmit on HandleSubmit function
function handleSubmit(values: z.infer<typeof formSchema>) {
try {
onSubmit(values as MyFormData)
form.reset()
toast.success("User data submitted successfully!")
} catch (error) {
console.error("Form submission error", error)
toast.error("Failed to submit the form. Please try again.")
}
}
// update onsubmit in form
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(handleSubmit)}
className="space-y-8 ...
// Include a dynamic button for creating or updating user data
<Button type="submit">{initialData ? "Update" : "Create"}</Button>
Create a TypeScript interface to define the structure of form data, specifying field names and their respective data types for better type safety and maintainability.
export interface MyFormData {
id: string;
name_4603829743: string;
name_0878515932: number;
name_0706064476: string;
name_6646786819: string;
}
Implement a column component using `@tanstack/react-table` to display and manage structured data. Leverage the `MyFormData` type to define column configurations, ensuring clarity and type safety.
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { MyFormData } from "./types";
export const createColumns = (): ColumnDef<MyFormData>[] => {
const columns: ColumnDef<MyFormData>[] = [
{
accessorKey: "name_4603829743",
header: "Name",
},
{
accessorKey: "name_0878515932",
header: "Age",
},
{
accessorKey: "name_0706064476",
header: "Gender",
},
{
accessorKey: "name_6646786819",
header: "Email",
},
];
return columns;
};
Develop a data-table component to display structured data effectively. Integrate column definitions and data handling to create an interactive and user-friendly table.
"use client";
import { useState } from "react";
import {
ColumnDef,
flexRender,
ColumnFiltersState,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
useReactTable,
SortingState,
getSortedRowModel,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
state: {
columnFilters,
},
});
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<Input
placeholder="Filter..."
// value={(table.getColumn("name_4603829743")?.getFilterValue() as string) ?? ""}
// onChange={(event) =>
// table.getColumn("name_4603829743")?.setFilterValue(event.target.value)
// }
className="max-w-sm"
/>
<div className="flex items-center gap-4">
</div>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
);
}
Implement a page component that combines and renders the form and data-table components to create a cohesive user interface.
"use client";
import { useState } from "react";
import { createColumns } from "./column";
import { DataTable } from "./data-table";
import { MyFormData } from "./types";
const initialData: MyFormData[] = [
{
id: "1",
name_4603829743: "Jane Doe",
name_0878515932: 30,
name_0706064476: "female",
name_6646786819: "jane@test.com",
},
];
export default function TablePage() {
const [data, setData] = useState<MyFormData[]>(initialData);
const columns = createColumns();
return (
<div className="container mx-auto py-10">
<DataTable
columns={columns}
data={data}
/>
</div>
);
}