Adding image control fields in Block Content in Sanity

5 replies
Last updated: May 7, 2021
Are there any demo implementations of Block Content where there's added more controls for the placement of images within the Block Content? I'm thinking left/right alignment or centering, relative size of image (percentage), etc.? What is a recommended way of doing this?
AI Update

Looking at adding image alignment controls (left/right, center, sizing) to Block Content/Portable Text, there are a few solid approaches. The community has worked through this exact challenge since Portable Text doesn't include alignment options by default.

The most common pattern is to create a custom image object within your Portable Text array that includes alignment and sizing options. Here's a working example from the Sanity community discussion:

{
  title: 'Body Content',
  name: 'body',
  type: 'array',
  of: [
    {
      type: 'block',
    },
    {
      type: 'object',
      title: 'Image',
      name: 'blockImage',
      fields: [
        {
          type: 'image',
          name: 'image',
          options: {
            hotspot: true
          }
        },
        {
          type: 'string',
          name: 'alignment',
          title: 'Alignment',
          options: {
            list: [
              { title: 'Left', value: 'left' },
              { title: 'Center', value: 'center' },
              { title: 'Right', value: 'right' }
            ],
            layout: 'radio',
            direction: 'horizontal'
          }
        },
        {
          type: 'number',
          name: 'width',
          title: 'Width (%)',
          validation: Rule => Rule.min(10).max(100)
        },
        {
          type: 'string',
          name: 'alt',
          title: 'Alt text'
        }
      ]
    }
  ]
}

This approach lets editors insert images between text blocks and choose alignment (left/right/center) and sizing. The image sits as its own block in the content flow, which works well for float-left, float-right, and centered images.

Rendering on the Frontend

When rendering with @portabletext/react or similar libraries, you'd handle the custom block:

import { PortableText } from '@portabletext/react'
import imageUrlBuilder from '@sanity/image-url'

const builder = imageUrlBuilder(client)
const urlFor = (source) => builder.image(source)

const components = {
  types: {
    blockImage: ({ value }) => (
      <figure 
        className={`image-${value.alignment}`}
        style={{ 
          width: value.width ? `${value.width}%` : 'auto',
          float: value.alignment === 'left' || value.alignment === 'right' 
            ? value.alignment 
            : 'none'
        }}
      >
        <img 
          src={urlFor(value.image)} 
          alt={value.alt}
        />
      </figure>
    )
  }
}

<PortableText value={content} components={components} />

Making it Preview Better in Studio

You can customize how custom blocks appear in the Portable Text Editor to show a preview of your image blocks. This helps editors see how alignment will look:

{
  type: 'object',
  name: 'blockImage',
  // ... fields as above
  preview: {
    select: {
      image: 'image',
      alignment: 'alignment',
      width: 'width'
    },
    prepare({ image, alignment, width }) {
      return {
        title: `Image (${alignment}, ${width || 100}%)`,
        media: image
      }
    }
  }
}

For even better in-editor visualization, check out the guide on adding custom YouTube embeds which shows the pattern for rendering custom components directly in the editor.

Using Preview Pane for Real-Time Feedback

As the community strongly recommends, the iframe-pane plugin is excellent for seeing real-time previews of how your alignment and sizing choices render on your actual site. This gives editors immediate visual feedback side-by-side with the content they're editing, including mobile and desktop views.

Alternative: Inline Images

For truly inline images (flowing within text like an emoji), you can use the block type's of property to add inline objects with options.inline: true. However, this is less common for alignment use cases since floated/aligned images typically work better as standalone blocks between paragraphs rather than mid-sentence.

Demo Implementations

While there isn't an official Sanity demo specifically for image alignment, you can find community examples in:

The key insight: separation of concerns is maintained because you're storing structured data (alignment: "left", width: 50) rather than inline styles or HTML. Your frontend then interprets this data to apply appropriate CSS classes or styles, giving you flexibility to change presentation without touching content. This is the Portable Text philosophy in action.

Show original thread
5 replies
Hey Øystein 👋
I usually use a
string
field with a pre-defined set of options where I can map out the possible style variations. Here's a full example:
export default {
  name: 'imageBlock',
  title: 'Image block',
  type: 'object',
  fields: [
    {
      name: 'image',
      title: 'Image',
      type: 'image',
      validation: Rule => Rule.required(),
    },
    {
      name: 'alt',
      title: 'Description of the image for screen readers',
      description: '⚡ Optional but highly encouraged to make content more accessible for visually impaired folks.',
      type: 'string',
    },
    {
      name: 'size',
      title: 'Size',
      type: 'string',
      initialValue: 'md',
      options: {
        list: [
          {
            value: 'sm',
            title: 'Small'
          },
          {
            value: 'md',
            title: 'Medium'
          },
          {
            value: 'lg',
            title: 'Large'
          },
        ]
      }
    },
    {
      name: 'alignment',
      title: 'Alignment',
      type: 'string',
      initialValue: 'center',
      options: {
        layout: 'radio',
        list: [
          {
            value: 'left',
            title: 'Left'
          },
          {
            value: 'center',
            title: 'Center'
          },
          {
            value: 'right',
            title: 'Right'
          },
        ]
      }
    },
  ],
}
Hey Øystein 👋
I usually use a
string
field with a pre-defined set of options where I can map out the possible style variations. Here's a full example:
export default {
  name: 'imageBlock',
  title: 'Image block',
  type: 'object',
  fields: [
    {
      name: 'image',
      title: 'Image',
      type: 'image',
      validation: Rule => Rule.required(),
    },
    {
      name: 'alt',
      title: 'Description of the image for screen readers',
      description: '⚡ Optional but highly encouraged to make content more accessible for visually impaired folks.',
      type: 'string',
    },
    {
      name: 'size',
      title: 'Size',
      type: 'string',
      initialValue: 'md',
      options: {
        list: [
          {
            value: 'sm',
            title: 'Small'
          },
          {
            value: 'md',
            title: 'Medium'
          },
          {
            value: 'lg',
            title: 'Large'
          },
        ]
      }
    },
    {
      name: 'alignment',
      title: 'Alignment',
      type: 'string',
      initialValue: 'center',
      options: {
        layout: 'radio',
        list: [
          {
            value: 'left',
            title: 'Left'
          },
          {
            value: 'center',
            title: 'Center'
          },
          {
            value: 'right',
            title: 'Right'
          },
        ]
      }
    },
  ],
}
Glad to explain parts that aren't clear 🙂
I see. So you just add the parameters you need as fields next to the image object? Do you do anything to change how the images within the block content is displayed in Sanity Studio?
Yup, I usually use a custom preview component that implements these parameters to let editors get a sense of what it'll look like in the front-end. If you have a React component library for your front-end, you could even render it wholesale in the studio!

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?