Joint session with Vercel: How to build intelligent storefronts (May 15th)

Sanity Tab Block Plugin

A Sanity Studio plugin that lets you create tabbed content blocks with customisable styles, layouts and rich text editing capabilities.

By Multidots

Install command

npm i @multidots/sanity-plugin-tab-block

@multidots/sanity-plugin-tab-block

A comprehensive tab block plugin for Sanity Studio that provides dynamic, customizable tab interfaces with rich content support.

Features

🎯 Core Functionality

  • Multiple Tabs: Support for multiple tabs per block with validation
  • Rich Content: Portable Text support with headings, lists, links, and formatting
  • Flexible Layouts: Horizontal and vertical tab orientations with intelligent mobile adaptation
  • Responsive Design: Comprehensive responsive behavior across all device sizes
  • Full ARIA Compliance: Complete role, aria-selected, aria-controls, aria-labelledby, and tablist attributes

🎨 Customization Options

  • Layout Control: Choose between horizontal and vertical tab arrangements
  • Advanced Alignment Settings: Independent control over title, tab titles, content, and vertical alignment
  • Comprehensive Color Theming: Full color customization for all visual elements
    • Block title color
    • Tab title colors (active/inactive/hover states)
    • Tab background colors (active/inactive states)
    • Content text and background colors
    • Tab hover colors

Installation

npm install @multidots/sanity-plugin-tab-block

Quick Start

1. Add the Plugin to Your Sanity Config

import {defineConfig} from 'sanity'
import {tabBlockPlugin} from '@multidots/sanity-plugin-tab-block'

export default defineConfig({
  // ... other config
  plugins: [
    tabBlockPlugin(),
    // ... other plugins
  ],
})

2. Add to Your Schema

// In your document schema
export default defineType({
  name: 'page',
  title: 'Page',
  type: 'document',
  fields: [
    defineField({
          name: "tabBlock",
          type: "tabBlock",
    }),
  ],
})

3. Render in Your Frontend

Update GROQ Queries to Include Tab Block

export const getPageQuery = defineQuery(`
  *[_type == 'page' && slug.current == $slug][0]{
    _id,
    _type,
    name,
    slug,
    tabBlock,
  }
}`);

Create a Component Wrapper

For a clean and maintainable frontend implementation, create a dedicated component wrapper first, then use it in your pages. This approach provides better code organization and reusability.

First, create a Tab.tsx component in your components directory:

  'use client'

import { TabBlock as TabBlockComponent, TabBlockData } from "@multidots/sanity-plugin-tab-block"
  import { PortableText } from 'next-sanity'

  type TabProps = {
      tab: TabBlockData
  }

  const Tab = ({ tab }: TabProps) => {
      return (
          <TabBlockComponent
              tab={tab}
              PortableText={(props: any) => <PortableText {...props} />}
          />
      )
  }

  export default Tab

Render in Your Page Example

Use in Your Page Component

import { TabBlockData } from '@multidots/sanity-plugin-tab-block';
import Tab from '@/components/Tab'

// Add code to get the page data

export default async function Page({ params }: RouteProps) {
    const { data: page } = await getPage(params);
    return  (
        <>
            {page?.tabBlock && (
                <Tab tab={page?.tabBlock as TabBlockData} />
            )}
        </>
    ) ;
}
            

Configuration Options

Schema Fields

FieldTypeDescriptionGroup
titlestringOptional title for the entire tab block-
tabsarrayArray of tab objects (1-10 tabs)Tabs & Content
layoutstring'horizontal' or 'vertical'Layout & Alignment
titleAlignmentstring'left', 'center', or 'right'Layout & Alignment
tabTitleAlignmentstring'left', 'center', or 'right'Layout & Alignment
tabTitleVerticalAlignmentstring'start', 'center', or 'end' (vertical layout only)Layout & Alignment
tabContentAlignmentstring'left', 'center', or 'right'Layout & Alignment

Color Customization

Color FieldDescriptionUsage
titleColorMain block title colorApplied to the main title text
tabTitleColorInactive tab title colorDefault color for tab buttons
tabTitleHoverColorTab title hover background colorBackground color when hovering over tabs
activeTabTitleColorActive tab title colorText color for the currently active tab
tabBackgroundColorTab button background colorDefault background for tab buttons
activeTabBackgroundColorActive tab background colorBackground color for the active tab
tabContentColorTab content text colorText color within tab content areas
tabContentBackgroundColorTab content area background colorBackground color for the content area

Tab Object Structure

Each tab in the tabs array contains:

{
  tabTitle: string        // Tab button text (required, 1-50 characters)
  content: PortableText[] // Rich text content (required)
}

Content Features

Supported Rich Text Elements

  • Headings: H1, H2, H3, H4
  • Text Formatting: Bold, italic, underline
  • Lists: Bulleted and numbered lists
  • Blockquotes: Quote styling

Links support:

  • HTTP/HTTPS URLs
  • Email addresses (mailto:)
  • Phone numbers (tel:)

Component Props

TabBlock Component

interface TabBlockProps {
  tab: TabBlockData           // The tab block data from Sanity
  PortableText?: React.ComponentType<{value: unknown}>  // Portable Text renderer
  className?: string          // Additional CSS classes
}

TabBlockData Interface

interface TabBlockData {
  title?: string
  tabs?: Array<{
    tabTitle?: string
    content?: unknown[]
    _type?: string
    _key?: string
  }>
  layout?: 'horizontal' | 'vertical'
  titleAlignment?: 'left' | 'center' | 'right'
  tabTitleAlignment?: 'left' | 'center' | 'right'
  tabTitleVerticalAlignment?: 'start' | 'center' | 'end'
  tabContentAlignment?: 'left' | 'center' | 'right'
  // Color properties
  titleColor?: {hex: string}
  tabTitleColor?: {hex: string}
  tabTitleHoverColor?: {hex: string}
  activeTabTitleColor?: {hex: string}
  tabBackgroundColor?: {hex: string}
  activeTabBackgroundColor?: {hex: string}
  tabContentColor?: {hex: string}
  tabContentBackgroundColor?: {hex: string}
}

Styling and Customization

Responsive Design

The component automatically adapts to different screen sizes:

  • Desktop & Tablet: Maintains chosen layout (horizontal/vertical)
  • Mobile: Vertical tabs convert to horizontal scrollable navigation

Custom Styling

You can override styles by:

  1. Passing a className prop to the component
  2. Using CSS specificity to override built-in styles
  3. Customizing colors through the Sanity interface
  4. Targeting specific responsive breakpoints

CSS Classes

The component generates these CSS classes:

  • .tab-block - Main container
  • .tab-block-title - Block title
  • .tab-container - Tab layout container (.horizontal or .vertical)
  • .tab-nav - Tab navigation container
  • .tab-nav-item - Individual tab buttons (.active for current tab)
  • .tab-content - Content area
  • .tab-panel - Individual content panels (.hidden for inactive panels)

Screenshots

Backend Settings:

https://share.cleanshot.com/vyGd88Rybns7g2vwmxZW

Frontend Horizontal View:

https://share.cleanshot.com/3x4DB3JYSZCFyhBR8MK0

Frontend Vertical View:

https://share.cleanshot.com/0Q5yBP3KTlszMFMST5Ft

Development

Testing in Sanity Studio

See Testing a plugin in Sanity Studio for instructions on how to run this plugin with hotreload in your studio.

Examples

Advanced Configuration

// Full customization with all available options
{
  _type: 'tabBlock',
  title: 'Service Information',
  layout: 'vertical',
  titleAlignment: 'center',
  tabTitleAlignment: 'left',
  tabTitleVerticalAlignment: 'start',
  tabContentAlignment: 'left',
  titleColor: {hex: '#2D3748'},
  tabTitleColor: {hex: '#4A5568'},
  tabTitleHoverColor: {hex: '#F7FAFC'},
  activeTabTitleColor: {hex: '#3182CE'},
  tabBackgroundColor: {hex: 'transparent'},
  activeTabBackgroundColor: {hex: '#EBF8FF'},
  tabContentColor: {hex: '#2D3748'},
  tabContentBackgroundColor: {hex: '#F7FAFC'},
  tabs: [
    {
      tabTitle: 'Overview',
      content: [
        {
          _type: 'block',
          children: [{text: 'Comprehensive service overview...'}]
        }
      ]
    },
    {
      tabTitle: 'Features',
      content: [
        {
          _type: 'block',
          children: [{text: 'Key features and benefits...'}]
        }
      ]
    }
  ]
}

Troubleshooting

Common Issues

Issue: Tabs not rendering properly

  • Solution: Ensure you're passing the PortableText component prop

Issue: Styles not applying

  • Solution: Check that color values are in the correct {hex: string} format

Issue: Content not displaying

  • Solution: Verify that tab content is properly structured Portable Text

Performance Tips

  1. Tab Limits: Keep tabs to 10 or fewer for optimal performance and user experience
  2. Content Optimization: Use lazy loading for heavy content in tabs
  3. Mobile Considerations: Consider tab content length and title length for mobile users
  4. Color Usage: Use the built-in color customization rather than custom CSS for better performance
  5. Responsive Testing: Test on various devices to ensure optimal responsive behavior

License

MIT © Multidots


Related contributions