Portable Text Editor

repository·main·Indexed 42 days ago

https://github.com/portabletext/editor

A headless, schema-driven block content editor for React designed for the Portable Text open specification. It features a Behavior API for state machine interaction, a plugin system for custom node registrations via NodePlugin, and tools for schema definition using defineSchema. The ecosystem includes @portabletext/editor for core functionality, @portabletext/toolbar for UI components, and block-tools for HTML conversion and normalization.

Tokens
128.9K
Snippets
344
Records
485
Agent score
51%

What's inside Portable Text Editor

  1. What is Racetrack

    main
    Racetrack is a hosted playground and scenario-driven test runner designed specifically for Portable Text Editor plugins. It provides tools for visual scenario authoring, an in-browser scenario runner, a plugin garage, and an engine viewer to help developers test and showcase their editor extensions.
  2. Overview of the Portable Text Editor

    main

    The Portable Text Editor (@portabletext/editor) is a headless, schema-driven block content editor designed for React. It is built around the Portable Text specification and follows a 'bring your own UI' philosophy: the editor manages the content state and editing logic, while you provide the visual implementation.

    Key architectural components include:

    • Schema: Defines the allowed styles, decorators, annotations, lists, block objects, and inline objects.
    • Rendering: Controlled via node registrations and render props on the PortableTextEditable component.
    • Containers: Allow complex structures (like callouts, code blocks, or tables) to host editable block content.
    • Behavior API: An extensible system for defining how users interact with the content.
    • Plugins: A modular system to extend editor functionality.
    • @portabletext/toolbar: A companion package providing headless hooks for building custom toolbar interfaces.
  3. Overview of the Portable Text Editor Monorepo

    main

    The Portable Text Editor monorepo provides @portabletext/editor, a headless, schema-driven block content editor for React. Unlike renderers (which are found in separate repositories like @portabletext/react), this project focuses on the editing experience. It is designed so that developers provide the UI, while the editor handles the underlying block content logic.

    Key architectural components include:

    • Core: The editor engine, schema definition, and toolbar hooks.
    • Plugins: Extensions for features like Markdown shortcuts, emoji pickers, tables, and typography.
    • Utilities: Libraries for converting between Portable Text and HTML/Markdown, keyboard shortcuts, and testing utilities.
  4. Supported Markdown features for Portable Text conversion

    main

    The @portabletext/markdown package (and related conversion logic) supports a wide range of Markdown syntax to transform it into structured Portable Text. Supported features include:

    • Text Formatting: Bold (**bold**), italic (_italic_), inline code (`code`), and strikethrough (~~strikethrough~~). The parser supports nested formatting (e.g., bold and italic combined).
    • Links and Images:
      • Inline links: [text](url)
      • Reference-style links: [text][ref1] with definitions like [ref1]: url
      • Implicit references
      • Block images: ![alt](url)
      • Inline images: text ![alt](url) text
      • Autolinks: <https://example.com> and <mailto:email@example.com>
    • Code Blocks: Fenced code blocks (with syntax highlighting support) and indented code blocks.
    • Blockquotes: Single and nested blockquotes.
    • Lists:
      • Unordered lists (- or *)
      • Ordered lists (1.)
      • Mixed lists (nesting ordered lists inside unordered lists and vice versa)
    • Tables: Standard Markdown table syntax.
    • Horizontal Rules: Separators using ---.
    • Headings: All levels from # Heading 1 to ###### Heading 6.
    • Other: Hard line breaks (\), HTML passthrough (e.g., <div> or <span>), and special character handling (&amp;, &lt;, &gt;).
  5. Supported Markdown features in Portable Text conversion

    main

    The @portabletext/markdown package (and related conversion logic) supports a wide range of Markdown syntax to transform it into structured Portable Text. Supported features include:

    • Text Formatting: Bold (**), Italic (_), Inline Code (`), and Strikethrough (~~). The parser supports deep nesting (e.g., bold inside italic).
    • Links and Images: Inline links, reference-style links, and both block and inline images.
    • Code Blocks: Fenced code blocks (with syntax highlighting) and indented code blocks.
    • Blockquotes: Single and nested blockquotes.
    • Lists: Unordered lists, ordered lists, and mixed lists (ordered parents with unordered children or vice versa) with deep nesting.
    • Tables: Standard Markdown tables.
    • Horizontal Rules: Separators using ---.
    • Headings: All levels from # (H1) to ###### (H6).
    • Advanced Elements: Autolinks (URLs and mailto:), hard line breaks, and HTML passthrough (preserving raw HTML tags).

    This comprehensive support ensures that the resulting Portable Text preserves both the visual structure and the semantic meaning of the original Markdown.

  6. Core concepts demonstrated in the Basic Editor example

    main

    The basic example serves as a reference for implementing a full-featured Portable Text editor using the following patterns:

    • Schema Definition: Using defineSchema to configure decorators, annotations, styles, lists, block objects (e.g., images), and inline objects (e.g., stock tickers).
    • Node Registrations: Mounting specific node types using NodePlugin. This involves using registration functions such as:
      • defineTextBlock
      • defineBlockObject
      • defineInlineObject
      • defineDecorator
      • defineAnnotation
    • List Management: Implementing list numbering via @portabletext/plugin-list-index.
    • Toolbar Implementation: Building custom toolbars that toggle marks, styles, and lists by using the useEditor and useEditorSelector hooks, combined with selectors from @portabletext/editor/selectors.
    • Value Monitoring: Reading and reacting to editor value changes using EventListenerPlugin to render live JSON previews.
  7. What is Portable Text?

    main

    Portable Text is a JSON-based specification for structured block content. Instead of using HTML strings or Markdown, content is represented as an array of typed blocks. Each block has a _type property and carries its own specific data.

    Key characteristics:

    • Array of Blocks: The document is a top-level array containing various block types (e.g., block, image, callToAction).
    • Typed Content: Every block identifies itself via _type, allowing serializers to know how to render it.
    • Structured Data: Because it is JSON, content is easily queryable and can be rendered into multiple formats (React, HTML, Markdown, PDF, etc.) using a serializer.
    [
      {
        "_type": "block",
        "style": "h1",
        "children": [{ "_type": "span", "text": "Why Portable Text?" }]
      },
      {
        "_type": "image",
        "url": "photo.jpg",
        "alt": "A mountain landscape"
      }
    ]
  8. What are Containers in Portable Text?

    main

    A container is a block object that holds editable block content. It is defined by having an array field whose members include text blocks or further containers. This allows you to nest editable content inside custom structures like callouts, code blocks, or tables without leaving the Portable Text format.

    In the underlying data, a container is just a plain Portable Text object where one field holds an array of blocks. Serializers and queries treat them as nested Portable Text.

    {
      "_type": "callout",
      "_key": "a1b2c3",
      "tone": "note",
      "content": [
        {
          "_type": "block",
          "_key": "d4e5f6",
          "children": [
            {
              "_type": "span",
              "_key": "g7h8i9",
              "text": "Editable text inside the callout.",
              "marks": []
            }
          ],
          "markDefs": [],
          "style": "normal"
        }
      ]
    }
  9. Understand the Portable Text Editor building blocks

    main

    The Portable Text Editor is composed of several interconnected parts that define how content is structured and displayed. Key components include:

    • Schema: Defines the structure and rules for content.
    • Styles: Define the visual representation of elements.
    • Decorators: Used to augment the visual appearance of nodes.
    • Annotations: Metadata attached to text ranges.
    • Lists, Block objects, and Inline objects: The fundamental structural units of the Portable Text format.

    Understanding how these pieces connect is essential for configuring the editor's content model.

  10. Extend the editor with Plugins

    main

    The editor is extensible via a plugin system. Available plugin types and features include:

    • Markdown shortcuts: Automatic formatting based on markdown syntax.
    • Emoji picker: UI for inserting emojis.
    • Input rules: Automatic transformations during typing.
    • Character pair decorators: Special handling for specific character sequences.
    • Paste link: Enhanced link handling during paste operations.
    • One-line mode: Specialized editing mode.
    • Typeahead picker: Autocomplete/suggestion UI.
    • Typography transforms: Automatic typographic improvements.
  11. Understand the Portable Text data model

    main

    Portable Text stores content as structured JSON data rather than HTML strings or Markdown. This approach treats content as a sequence of typed blocks, making it platform-agnostic and highly queryable.

    Key Characteristics

    • No rendering assumptions: The data doesn't assume a target format (like HTML). You can render the same JSON to React, HTML, PDF, or plain text.
    • Structured Blocks: Content is organized into blocks (e.g., _type: 'block') which contain children (spans of text) and markDefs (definitions for inline annotations like links).
    • Extensibility: You can define custom block types (e.g., callToAction, imageGallery) or custom inline annotations (e.g., stockTicker) without breaking existing renderers.
    • Queryability: Because it is JSON, you can perform precise queries (e.g., "find all blocks containing a link to /docs") without fragile regex or HTML parsing.
    [
      {
        "_type": "block",
        "style": "normal",
        "children": [
          {"_type": "span", "text": "Read the "},
          {
            "_type": "span",
            "text": "documentation",
            "marks": ["a1b2c3"]
          },
          {"_type": "span", "text": " for "},
          {
            "_type": "span",
            "text": "details",
            "marks": ["strong"]
          },
          {"_type": "span", "text": "."}
        ],
        "markDefs": [
          {
            "_key": "a1b2c3",
            "_type": "link",
            "href": "/docs"
          }
        ]
      }
    ]