Slate Portive
  • introduction
  • Introduction
  • Guides
    • Getting Started
    • Saving Document
    • Uploading Files
  • Customizing
    • Custom Image Elements
    • Custom File Elements
  • Reference
    • Editor
    • Origin
    • TypeScript
    • Premade Elements
  • Auth
    • AuthToken
Powered by GitBook
On this page
  • Custom Image Type
  • Custom Image Component
  • Customize createImageFileElement callback
Edit on GitHub
  1. Customizing

Custom Image Elements

Learn how to create a Custom Image Component. For this example, we'll modify the ImageBlock Component by adding a title attribute to the Image.

Let's start by looking at then modifing the type of the ImageBlockElement.

Custom Image Type

🌞 Even if you aren't using TypeScript, we recommend reading this section. You can probably follow the meaning of the type declarations (e.g. originKey: string means the originKey property takes a string type value).

Here is the type for the ImageBlockElement.

export type ImageBlockElement = {
  type: "image-block"
  originKey: string
  originSize: [number, number]
  size: [number, number]
  children: [{ text: "" }]
}

This Element has its type set to "image-block". Also, this is a void Element, so it has children which is [{ text: "" }] (a requirement for void Elements).

It also has three other properties that are meaningful: An originKey, originSize and size. These properties are part of the ImageFileInterface and are required in order to use the HostedImage subcomponent which takes care of resizing, and showing the upload progress and error state.

Here is the ImageFileInterface:

export interface ImageFileInterface {
  originKey: string
  originSize: [number, number]
  size: [number, number]
}

Although the ImageFileInterface is the minimum requirement for a HostedImage we can add other properties we desire to the Element.

Let's create a new Element "titled-image-block" which renders an <img> with a title attribute.

Here's an Element type definition that includes a title property:

export type TitledImageBlockElement = {
  type: "titled-image-block"
  title: string // ✅ Add a `title` property for our titled image
  originKey: string
  originSize: [number, number]
  size: [number, number]
  children: [{ text: "" }]
}

Custom Image Component

Here's the Preset ImageBlock Component.

import { RendeElementPropsFor, HostedImage } from "slate-portive"

export function ImageBlock({
  attributes,
  element,
  children,
}: RenderElementPropsFor<ImageBlockElement>) {
  return (
    <div {...attributes} style={{ margin: "8px 0" }}>
      <HostedImage
        element={element}
        style={{ borderRadius: element.size[0] < 100 ? 0 : 4 }}
      />
      {children}
    </div>
  )
}

It may seem small. This is because the HostedImage sub-component takes care of most of the hard work:

  • Resizing with drag handles

  • Showing the width/height during resize

  • Showing a progress bar while uploading

  • Showing an Error when an upload fails

  • Showing retina images for high DPI devices and normal resolution images for low DPI devices

From the perspective of the ImageBlock Component, we can treat it just like an img tag and it can take any img attributes like a "title" attribute for example.

Let's modify this to create our Custom TitledImageBlock Component:

import { RendeElementPropsFor, HostedImage } from "slate-portive"

export function TitledImageBlock({
  attributes,
  element,
  children,
}: // ✅ Change `ImageBlockElement` to `TitledImageBlockElement`
RenderElementPropsFor<TitledImageBlockElement>) {
  return (
    <div {...attributes} style={{ margin: "8px 0" }}>
      {/* ✅ Add `element.title` */}
      <HostedImage
        element={element}
        style={{ borderRadius: element.size[0] < 100 ? 0 : 4 }}
        title={element.title}
      />
      {children}
    </div>
  )
}

Now our Custom Image can render the image with the title attribute.

Customize createImageFileElement callback

const editor = withPortive(reactEditor, {
  createImageFileElement: createImageBlock,
  // ...
})

Here's the createImageBlock method passed to the createImageFileElement option:

export function createImageBlock(
  e: CreateImageFileElementEvent
): ImageBlockElement {
  return {
    type: "image-block",
    originKey: e.originKey, // ✅ sets originKey from the event
    originSize: e.originSize, // ✅ sets originSize from the event
    size: e.initialSize, // ✅ sets size from `initialSize` from the event
    children: [{ text: "" }],
  }
}

Here's what e which is of type CreateImageFileElementEvent looks like:

export type CreateImageFileElementEvent = {
  type: "image"
  originKey: string
  originSize: [number, number]
  initialSize: [number, number]
  file: File
}

export type CreateImageFileElement = (
  e: CreateImageFileElementEvent
) => Element & { originKey: string }

Let's use the file object for our TitledImageBlock:

export function createTitledImageBlock(
  e: CreateImageFileElementEvent
  // ✅ returns a `TitledImageBlockElement` instead
): TitledImageBlockElement {
  return {
    type: "titled-image-block",
    title: e.file.name, // ✅ set the initial title value to the filename
    originKey: e.originKey,
    originSize: e.originSize,
    size: e.initialSize,
    children: [{ text: "" }],
  }
}

Now it's just a matter of importing and using our new TitledImageBlock. Here's the full source code...

import {
  CreatedImageFileElementEvent,
  RendeElementPropsFor,
  HostedImage,
} from "slate-portive"

export type TitledImageBlockElement = {
  type: "titled-image-block"
  title: string // ✅ Add a `title` property for our titled image
  originKey: string
  originSize: [number, number]
  size: [number, number]
  children: [{ text: "" }]
}

export function createTitledImageBlock(
  e: CreateImageFileElementEvent
  // ✅ returns a `TitledImageBlockElement` instead
): TitledImageBlockElement {
  return {
    type: "titled-image-block",
    title: e.file.name, // ✅ set the initial title value to the filename
    originKey: e.originKey,
    originSize: e.originSize,
    size: e.initialSize,
    children: [{ text: "" }],
  }
}

export function TitledImageBlock({
  attributes,
  element,
  children,
}: // ✅ Change `ImageBlockElement` to `TitledImageBlockElement`
RenderElementPropsFor<TitledImageBlockElement>) {
  return (
    <div {...attributes} style={{ margin: "8px 0" }}>
      {/* ✅ Add `element.title` */}
      <HostedImage
        element={element}
        style={{ borderRadius: element.size[0] < 100 ? 0 : 4 }}
        title={element.title}
      />
      {children}
    </div>
  )
}
PreviousUploading FilesNextCustom File Elements

Last updated 2 years ago

When a user uploads an image, the createImageFileElement function passed to withPortive is called. In withPortive has these options:

You can learn more about file by reading the .

Getting Started
File MDN web docs