install and add @sanity/table
to plugins array in the sanity.config.ts
file.
// TableWidget.tsx
import { TablePreview } from "@sanity/table";
import { PreviewProps } from "sanity";
interface Table {
rows?: TableRow[];
title?: string;
}
interface TableValueProps {
table?: Table;
caption?: string;
}
export function TableWidget(props: TableValueProps & PreviewProps) {
const { table, caption, title, ...rest } = props;
const tablePreviewProps = { ...rest, rows: table?.rows || [] };
return (
<>
<div className="px-3">
<em className="not-italic text-sm font-semibold">
{caption ?? "Untitled Table"}
</em>
</div>
<TablePreview {...tablePreviewProps} description={caption} />
</>
);
}
Include table schema to schemas array
// schemas/table.ts
import { defineField, defineType } from "sanity";
import { TableWidget } from "@/app/components/widgets/TableWidget";
import { LuTable } from "react-icons/lu";
export const table = defineType({
name: "customTable",
title: "Table",
type: "object",
icon: LuTable,
fields: [
defineField({
name: "table",
title: "Table",
type: "table",
}),
defineField({
name: "caption",
type: "string",
title: "Caption",
description: "Provide an accessible description for this table",
}),
],
preview: {
select: {
table: "table",
caption: "caption",
},
},
components: {
preview: TableWidget,
},
});
import { defineArrayMember, defineType } from "sanity";
export default defineType({
name: "blockContent",
title: "Body",
type: "array",
description: "Write your content here",
of: [
defineArrayMember({
title: "Block",
type: "block",
styles: [
{ title: "Normal", value: "normal" },
{ title: "H2", value: "h2" },
{ title: "H3", value: "h3" },
{ title: "H4", value: "h4" },
{ title: "Quote", value: "blockquote" },
],
marks: {
decorators: [
{ title: "Strong", value: "strong" },
{ title: "Emphasis", value: "em" },
{ title: "Code", value: "code" },
],
},
}),
defineArrayMember({
type: "customTable", // Add table to block content
}),
],
});
// Table.tsx
import { TableValueProps } from "@/types";
export default function Table({ value }: { value: TableValueProps }) {
const { caption, table } = value;
const tableContent = table?.rows;
if (!tableContent || tableContent.length < 1) {
return <p>Table Data Missing</p>;
}
const [tableHeading, ...tableBody] = tableContent.map((t) => t.cells);
if (!tableHeading || tableBody.length < 1) {
return <p>Table Data must have at least one cell.</p>;
}
return (
<table className="border dark:border-zinc-800 border-zinc-200 w-full text-base my-4">
{caption && (
<caption className="text-lg font-incognito font-medium my-1">
{caption}
</caption>
)}
<thead className="bg-zinc-50 dark:bg-[#141414] border-b dark:border-zinc-800 border-zinc-200 text-left">
<tr className="divide-x divide-zinc-200 dark:divide-zinc-800">
{tableHeading.map((heading) => (
<th
key={heading}
scope="col"
className="font-medium text-lg font-incognito px-3 py-2"
>
{heading}
</th>
))}
</tr>
</thead>
<tbody>
{tableBody.map((row, index) => (
<tr
key={index}
className="divide-x divide-zinc-200 dark:divide-zinc-800 border dark:border-zinc-800 border-zinc-200"
>
{row.map((cell) => (
<td key={cell} className="px-3 py-2">
{cell}
</td>
))}
</tr>
))}
</tbody>
</table>
);
}
import { PortableTextComponents } from "@portabletext/react";
interface Table {
rows?: TableRow[];
title?: string;
}
interface TableValueProps {
table?: Table;
caption?: string;
}
export const CustomPortableText: PortableTextComponents = {
types: {
customTable: ({ value }: { value: TableValueProps }) => (
<Table value={value} />
),
},
}
Studio Preview | React Component |
---|---|
![]() |
![]() |