Shared Schemas

If you're not using our latest Next.js Sanity Starter

Skip this section if you're using our latest Next.js Sanity Starter. The shared schemas are already included in the starter template.

  • Portable Text

sanity/schemas/blocks/shared/block-content.ts:

import { defineType, defineArrayMember } from "sanity";
import { VideoIcon } from "@radix-ui/react-icons";
import { YouTubePreview } from "@/sanity/schemas/previews/youtube-preview";
 
export default defineType({
  title: "Block Content",
  name: "block-content",
  type: "array",
  of: [
    defineArrayMember({
      title: "Block",
      type: "block",
      styles: [
        { title: "Normal", value: "normal" },
        { title: "H1", value: "h1" },
        { title: "H2", value: "h2" },
        { title: "H3", value: "h3" },
        { title: "H4", value: "h4" },
        { title: "Quote", value: "blockquote" },
      ],
      lists: [
        { title: "Bullet", value: "bullet" },
        { title: "Number", value: "number" },
      ],
      marks: {
        decorators: [
          { title: "Strong", value: "strong" },
          { title: "Emphasis", value: "em" },
        ],
        annotations: [
          {
            title: "URL",
            name: "link",
            type: "object",
            fields: [
              {
                title: "URL",
                name: "href",
                type: "url",
                validation: (Rule) =>
                  Rule.uri({
                    allowRelative: true,
                    scheme: ["http", "https", "mailto", "tel"],
                  }),
              },
            ],
          },
        ],
      },
    }),
    defineArrayMember({
      type: "image",
      options: { hotspot: true },
      fields: [
        {
          name: "alt",
          type: "string",
          title: "Alternative Text",
        },
      ],
    }),
    defineArrayMember({
      name: "youtube",
      title: "YouTube Video",
      type: "object",
      icon: VideoIcon,
      fields: [
        {
          name: "videoId",
          title: "Video ID",
          type: "string",
          description: "YouTube Video ID",
        },
      ],
      preview: {
        select: {
          title: "videoId",
        },
      },
      components: {
        preview: YouTubePreview,
      },
    }),
  ],
});
  • Youtube Preview

sanity/schemas/previews/youtube-preview.tsx:

import type { PreviewProps } from "sanity";
import { Flex, Text } from "@sanity/ui";
import YouTubePlayer from "react-player/youtube";
import { VideoIcon } from "@radix-ui/react-icons";
 
export function YouTubePreview(props: PreviewProps) {
  const { title: videoId } = props;
 
  return (
    <Flex padding={3} align="center" justify="center">
      {typeof videoId === "string" ? (
        <YouTubePlayer url={`https://www.youtube.com/watch?v=${videoId}`} />
      ) : (
        <Flex align="center" justify="center">
          <VideoIcon />
          <Text>Add a YouTube Video ID</Text>
        </Flex>
      )}
    </Flex>
  );
}
  • Link with shadcn/ui button variant

sanity/schemas/blocks/shared/link.ts:

import { defineField, defineType } from "sanity";
 
export default defineType({
  name: "link",
  type: "object",
  title: "Link",
  fields: [
    defineField({
      name: "title",
      type: "string",
    }),
    defineField({
      name: "href",
      title: "href",
      type: "string",
    }),
    defineField({
      name: "target",
      type: "boolean",
      title: "Open in new tab",
    }),
    defineField({
      name: "buttonVariant",
      type: "button-variant",
      title: "Button Variant",
    }),
  ],
});
  • Layout Variants

sanity/schemas/blocks/shared/layout-variant.ts:

import { defineType } from "sanity";
 
export const STACK_ALIGN = [
  { title: "Left", value: "left" },
  { title: "Center", value: "center" },
];
 
export const SECTION_WIDTH = [
  { title: "Default", value: "default" },
  { title: "Narrow", value: "narrow" },
];
 
export const COLS_VARIANTS = [
  { title: "2 Columns", value: "grid-cols-2" },
  { title: "3 Columns", value: "grid-cols-3" },
  { title: "4 Columns", value: "grid-cols-4" },
];
  • Color Variants from shadcn/ui

sanity/schemas/blocks/shared/color-variant.ts:

import { defineType } from "sanity";
 
export const COLOR_VARIANTS = [
  { title: "Background", value: "background" },
  { title: "Primary", value: "primary" },
  { title: "Secondary", value: "secondary" },
  { title: "Card", value: "card" },
  { title: "Accent", value: "accent" },
  { title: "Destructive", value: "destructive" },
  { title: "Muted", value: "muted" },
];
 
export const colorVariant = defineType({
  name: "color-variant",
  title: "Color Variant",
  type: "string",
  options: {
    list: COLOR_VARIANTS.map(({ title, value }) => ({ title, value })),
    layout: "radio",
  },
  initialValue: "background",
});
  • Button Variants from shadcn/ui

sanity/schemas/blocks/shared/button-variant.ts:

import { defineType } from "sanity";
 
export const BUTTON_VARIANTS = [
  { title: "Default", value: "default" },
  { title: "Destructive", value: "destructive" },
  { title: "Outline", value: "outline" },
  { title: "Secondary", value: "secondary" },
  { title: "Ghost", value: "ghost" },
  { title: "Link", value: "link" },
];
 
export const buttonVariant = defineType({
  name: "button-variant",
  title: "Button Variant",
  type: "string",
  options: {
    list: BUTTON_VARIANTS.map(({ title, value }) => ({ title, value })),
    layout: "radio",
  },
  initialValue: "default",
});
  • Section Padding

sanity/schemas/blocks/shared/section-padding.ts:

import { defineField, defineType } from "sanity";
 
export default defineType({
  name: "section-padding",
  type: "object",
  title: "Padding",
  description: "Add padding to the section. Based on design system spacing",
  fields: [
    defineField({
      name: "top",
      type: "boolean",
      title: "Top Padding",
    }),
    defineField({
      name: "bottom",
      type: "boolean",
      title: "Bottom Padding",
    }),
  ],
});