npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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

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-block

Or 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

Studio Interface

Frontend Output

Frontend Output Image Left Frontend Output Image Right

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 ImageTextBlockPlugin

TypeScript 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)

Contributor

Multidots

Visit Multidots's profile


📄 License

MIT © Multidots