sanity-io / sanity-plugin-markdown

Markdown support in the Sanity Studio
MIT License
50 stars 8 forks source link
team-ecosystem

sanity-plugin-markdown

This is a Sanity Studio v3 plugin. For the v2 version, please refer to the v2-branch.

What is it?

A Markdown editor with preview for Sanity Studio.

Supports Github flavored markdown and image uploads. You can either drag image(s) into the editor or click the bottom bar to bring up a file selector. The resulting image URL(s) are inserted with a default width parameter which you can change to your liking using the Sanity image pipeline parameters.

The current version is a wrapper around React SimpleMDE (EasyMDE) Markdown Editor, and by extension, EasyMDE.

example.png

Installation

Install sanity-plugin-markdown and easymde@2 (peer dependency).

npm install --save sanity-plugin-markdown easymde@2

Usage

Add it as a plugin in sanity.config.ts (or .js):

import {markdownSchema} from 'sanity-plugin-markdown'

export default defineConfig({
  // ...
  plugins: [markdownSchema()],
})

Then, declare a field in your schema to be markdown

const myDocument = {
  type: 'document',
  name: 'myDocument',
  fields: [
    {
      type: 'markdown',
      description: 'A Github flavored markdown field with image uploading',
      name: 'bio',
    },
  ],
}

Next.js compatability

Next.js without Next 13 app directory does not support css imports from node_modules.

To use this plugin in this context (pages directory), use the sanity-plugin-markdown/next import instead of sanity-plugin-markdown:

import {markdownSchema} from 'sanity-plugin-markdown/next'

Then, make sure to add

import 'easymde/dist/easymde.min.css'

to the top of pages/_app.tsx.

Customizing the default markdown input editor

The plugin takes an input config option that can be used in combination with the MarkdownInput export to configure the underlying React SimpleMDE component:

// CustomMarkdownInput.tsx
import {MarkdownInput, MarkdownInputProps} from 'sanity-plugin-markdown'

export function CustomMarkdownInput(props) {
  const reactMdeProps: MarkdownInputProps['reactMdeProps'] = useMemo(() => {
    return {
      options: {
        toolbar: ['bold', 'italic'],
        // more options available, see:
        // https://github.com/Ionaru/easy-markdown-editor#options-list
      },
      // more props available, see:
      // https://github.com/RIP21/react-simplemde-editor#react-simplemde-easymde-markdown-editor
    }
  }, [])

  return <MarkdownInput {...props} reactMdeProps={reactMdeProps} />
}

Set the plugin input option:

// studio.config.ts
import {markdownSchema} from 'sanity-plugin-markdown'
import {CustomMarkdownInput} from './CustomMarkdownInput'

export default defineConfig({
  // ... rest of the config
  plugins: [markdownSchema({input: CustomMarkdownInput})],
})

Customize editor for a single field

Implement a custom input similar to the one above, and use it as components.input on the field directly.

defineField({
  type: 'markdown',
  name: 'markdown',
  title: 'Markdown',
  components: {input: CustomMarkdownInput},
})

Customizing editor preview

One way to customize the preview that does not involve ReactDOMServer (used by React SimpleMDE) is to install marked and DOMPurify and create a custom preview:

npm i marked dompurify

Then use these to create a custom editor:

// MarkdownInputCustomPreview.tsx
import {MarkdownInput, MarkdownInputProps} from 'sanity-plugin-markdown'
import DOMPurify from 'dompurify'
import {marked} from 'marked'

export function CustomMarkdownInput(props) {
  const reactMdeProps: MarkdownInputProps['reactMdeProps'] = useMemo(() => {
    return {
      options: {
        previewRender: (markdownText) => {
          // configure as needed according to
          // https://github.com/markedjs/marked#docs
          return DOMPurify.sanitize(marked.parse(markdownText))
        },
        //customizing using renderingConfig is also an option
      },
    }
  }, [])

  return <MarkdownInput {...props} reactMdeProps={reactMdeProps} />
}

Use the component as described in previous sections.

Custom image urls

Provide a function to options.imageUrl that takes a SanityImageAssetDocument and returns a string.

The function will be invoked whenever an image is pasted or dragged into the markdown editor, after upload completes.

The default implementation uses

;(imageAsset) => `${imageAsset.url}?w=450`

Example imageUrl option

defineField({
  type: 'markdown',
  name: 'markdown',
  title: 'Markdown',
  options: {
    imageUrl: (imageAsset) => `${imageAsset.url}?w=400&h=400`,
  },
})

License

MIT-licensed. See LICENSE.

Develop & test

This plugin uses @sanity/plugin-kit with default configuration for build & watch scripts.

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

Release new version

Run "CI & Release" workflow. Make sure to select the main branch and check "Release new version".

Semantic release will only release on configured branches, so it is safe to run release on any branch.