@multidots/sanity-plugin-image-text-block
v1.0.4
Published
Plugin to create image text combination block inside pages or any other document types.
Downloads
8
Maintainers
Readme
Sanity Image Text Block Plugin
By Multidots
A Sanity Studio v3+ plugin for creating image and text combination blocks with flexible positioning and styling.
🚀 Features
- ✅ Image Positioning - Left or right side positioning
- ✅ Rich Content - Title, subtitle, and rich text descriptions
- ✅ Custom Buttons - Call-to-action buttons with custom styling
- ✅ Color Customization - Background, text, and button colors
- ✅ Content Alignment - Top or center alignment options
- ✅ Responsive Design - Works on all device sizes
- ✅ TypeScript Support - Fully typed
📦 Installation
npm install @multidots/sanity-plugin-image-text-blockOr with yarn:
yarn add @multidots/sanity-plugin-image-text-block🔧 Add to Sanity Studio
1. Add Plugin to Sanity Config
import { defineConfig } from 'sanity'
import { ImageTextBlockPlugin } from '@multidots/sanity-plugin-image-text-block'
export default defineConfig({
// ... other config
plugins: [
ImageTextBlockPlugin(),
// ... other plugins
],
})2. Use in Schema
// schemas/page.ts
import { defineType, defineField } from 'sanity'
export default defineType({
name: 'page',
title: 'Page',
type: 'document',
fields: [
defineField({
name: "imageTextBlock",
type: "ImageTextBlockType",
}),
],
})⚛️ Frontend Usage Example (Next.js)
1. Add Field to Your Query
First, add the imageTextBlock field to your page or post query:
// lib/sanity/queries.ts
export const getPageQuery = defineQuery(`
*[_type == 'page' && slug.current == $slug][0]{
_id,
_type,
name,
slug,
imageTextBlock,
}
`);2. Create ImageText Wrapper Component
Create a wrapper component to handle the data transformation:
// components/ImageText.tsx
'use client'
import { ImageTextBlock } from "@multidots/sanity-plugin-image-text-block"
import { PortableTextBlock } from "sanity"
import type { SanityImageSource } from '@sanity/image-url/lib/types/types'
import { client } from '@/lib/sanity/client/client'
interface ImageTextProps {
title?: string
subTitle?: string
description?: PortableTextBlock[] | string | null
mainImage?: SanityImageSource
imagePosition?: "left" | "right"
backgroundColor?: {
hex: string
}
textColor?: {
hex: string
}
contentAlignment?: 'top' | 'center'
button?: {
text?: string
link?: string
openInNewTab?: boolean
buttonBackgroundColor?: {
hex: string
}
buttonTextColor?: {
hex: string
}
}
}
function portableTextToPlainText(value: PortableTextBlock[] | string | null | undefined): string {
if (!value) return ''
if (typeof value === 'string') return value
try {
return value
.map(block => {
if (block?._type !== 'block' || !('children' in block)) return ''
// @ts-expect-error: children exists on block content
return (block.children || []).map((child: any) => child.text).join('')
})
.join('\n')
} catch {
return ''
}
}
export const ImageText = ({ mainImage, title, subTitle, description, imagePosition, button, backgroundColor, textColor, contentAlignment }: ImageTextProps) => {
const descriptionText = portableTextToPlainText(description)
const normalizedButton = {
text: button?.text ?? '',
link: button?.link ?? '',
openInNewTab: button?.openInNewTab ?? false,
buttonBackgroundColor: button?.buttonBackgroundColor ?? { hex: '#667eea' },
buttonTextColor: button?.buttonTextColor ?? { hex: '#ffffff' },
}
return (
<ImageTextBlock
title={title || ''}
subTitle={subTitle || ''}
description={descriptionText}
mainImage={mainImage}
imagePosition={imagePosition}
button={normalizedButton}
backgroundColor={backgroundColor}
textColor={textColor}
contentAlignment={contentAlignment}
sanityClient={client}
/>
)
}3. Use in Your Page Component
// app/[slug]/page.tsx
import { ImageText } from "@/components/ImageText";
export default function Page({ page }) {
return (
<main>
{page?.imageTextBlock && (
<ImageText
title={page.imageTextBlock.title}
subTitle={page.imageTextBlock.subTitle}
description={page.imageTextBlock.description}
mainImage={page.imageTextBlock.mainImage}
imagePosition={page.imageTextBlock.imagePosition}
button={page.imageTextBlock.button}
backgroundColor={page.imageTextBlock.backgroundColor?.hex ? { hex: page.imageTextBlock.backgroundColor.hex } : undefined}
textColor={page.imageTextBlock.textColor?.hex ? { hex: page.imageTextBlock.textColor.hex } : undefined}
contentAlignment={page.imageTextBlock.contentAlignment}
/>
)}
</main>
)
}4. Environment Variables (.env.local)
Make sure to set up your environment variables:
NEXT_PUBLIC_SANITY_PROJECT_ID=your-project-id
NEXT_PUBLIC_SANITY_DATASET=production🎛️ Plugin Settings and Screenshots
Available Fields in Sanity Studio
Content Fields
- Title - Main heading text
- Subtitle - Secondary heading text
- Description - Rich text content with formatting options
Image Fields
- Main Image - Image upload with alt text support
Styling Options
- Image Position - Choose left or right positioning
- Content Alignment - Top or center vertical alignment
- Background Color - Content area background color picker
- Text Color - Universal text color for all content
Button Configuration
- Button Text - Call-to-action button label
- Button Link - URL destination
- Open in New Tab - External link behavior
- Button Background Color - Custom button background
- Button Text Color - Custom button text color
Frontend Output Examples
Left Image Position:
[IMAGE] | Title
| Subtitle
| Description text here...
| [Button]Right Image Position:
Title | [IMAGE]
Subtitle |
Description... |
[Button] |📷 Screenshots
Backend Settings: https://share.cleanshot.com/mfSSvb0FXGR7TmVspQSM
Frontend Output Image Left: https://share.cleanshot.com/jV7DnbbKvw3bmTsWXDNB
Frontend Output Image Right: https://share.cleanshot.com/Dqkrdrd2ywkLCkzzwFCS
Studio Interface

Frontend Output

Demo Video
https://share.cleanshot.com/w0xnp7mbGV72Y7K7tYvW
🐛 Troubleshooting
Images not displaying
Problem: Images appear broken or don't load Solution: Ensure you're passing the Sanity client properly:
<ImageTextBlock
{...blockData}
sanityClient={sanityClient}
/>Button links not working
Problem: Button clicks don't navigate Solution: Ensure the link field includes protocol:
✅ https://example.com
✅ /internal-page
❌ example.com (missing protocol)📤 Exports
The plugin exports the following components and types:
Main Exports
// Plugin for Sanity Studio
export const ImageTextBlockPlugin: PluginOptions
// React component for frontend
export const ImageTextBlock: React.FC<ImageTextBlockProps>
// Default export (plugin)
export default ImageTextBlockPluginTypeScript Interfaces
interface ImageTextBlockProps {
title?: string
subTitle?: string
description?: any // Rich text from Sanity
mainImage?: SanityImageSource & {
alt?: string
}
imagePosition?: 'left' | 'right'
backgroundColor?: {
hex: string
}
textColor?: {
hex: string
}
contentAlignment?: 'top' | 'center'
button?: {
text: string
link: string
openInNewTab: boolean
buttonBackgroundColor?: {
hex: string
}
buttonTextColor?: {
hex: string
}
}
sanityClient?: any // Sanity client for image URL generation
}Usage Examples
// Import plugin for Sanity config
import { ImageTextBlockPlugin } from '@multidots/sanity-plugin-image-text-block'
// Import component for React/Next.js
import { ImageTextBlock } from '@multidots/sanity-plugin-image-text-block'
// Import default (same as plugin)
import ImageTextBlockPlugin from '@multidots/sanity-plugin-image-text-block'Develop & test
This plugin uses @sanity/plugin-kit for build & watch scripts.
Local development tips:
- npm run link-watch or npm run watch to develop against a local Studio
- Publish with npm publish (build runs on prepublishOnly)
