Skip to content

Conversation

@jonkafton
Copy link
Contributor

@jonkafton jonkafton commented Oct 31, 2025

What are the relevant tickets?

N/A

Description (What does it do?)

Not for merging - PR contains a quick implementation of the Tiptap Editor. If we proceed, we'll need to refactor to better encapsulate. Here we're using the Simple Editor Template, which ejects a lot of boilerplate!

  • Adds a page at /article/new to show the TipTap editor with live markdown preview.

  • Provides extensions so we can insert or components, with LearningResourceCard and the AskTIM drawer as examples. These serialize to markdown with tokens [[resource-card:14731]] and [[asktim]].

Screenshots (if appropriate):

image

How can this be tested?

A preview is up and running at https://demo-1-209718960498.us-east1.run.app/article/new

POC

This proof of concept evaluates the feasibility of integrating the Tiptap Editor into our React/Next.js codebase.

The integration was smooth and demonstrates strong extensibility. Custom React components (LearningResourceCard, AskTIM demonstrated in the PR) can be embedded as editor content via lightweight extensions. Demo Page.

Output can be serialized to Markdown, JSON, or HTML, making it compatible with both modern and legacy environments.

Overall, Tiptap appears to be a strong and viable candidate for adoption as our content editing framework. Its React integration, TypeScript support, and extensible architecture align well with our component-driven design system, offering a long-term path toward structured, reusable, and interactive editor curated content.

Observations

  • No major issues integrating Tiptap into our codebase. It's well documented and has first class support for Typescript, React and Next.js.

  • Tiptap is very extensible. Extensions allow us to make our library components available for insertion into the document. This is fairly repeatable for each component - we can write a utility to bridge between Tiptap and our components, or new components we create for composing a document. We would need to add inputs to components when in edit mode, for example to set the resource IDs on resource cards or to specify the style variants.

  • The editor interface works well and is performant. The library build in a suite of editor UI components, though it's very modular if we wanted to customize. Two templates are available out the box; the Simple Editor (used in the PR) and a Notion-like editor, which needs a paid subscription with developer seats (starts $59/month with 2 developer licenses).

  • Tiptap's default output is JSON (strict ProseMirror schema). This PR uses their Markdown extension for better portability. To render our custom components, consuming environments would need to be running React, or we would need to provide a wrapper utility for embedding components with vanilla JS. The markdown plugin is in beta.

Setup

Installation on React projects is straightforward with @tiptap/react 1. A basic editor without controls can be implemented with a few lines of code. React is not a hard dependency and Tiptap can run on plain JS 2. CLI tools are provided to scaffold the editor into projects and install components.

A range of extensions are available to extend functionality 3. This includes the StarterKit, which bundles extensions to provide a set of common editor functionality 4.

Two templates are available that provide a full editor chrome. The Simple Editor 5 is demonstrated in this PR. These are installed with a provided CLI script. This ejects a lot of code to the project (140+ files in this PR).

Extensibility

Tiptap introduces Nodes (content types - headings, paragraphs, lists), Marks (text modifiers - bold, italic, highlight) and Functionality extensions (tools - drag/drop, comments, AI generation). Each provide factory methods to create custom extensions 6. This enables us to make our existing components available for editors to add to their content or provide any set of additional components for composing a page. The friction for adding these is fairly low:

  • Define an extension/node type.
  • Attach a view (ie. a React renderer).
  • Describe HTML serialization.
  • Optionally, provide a Mardown tokenizer 7 and parser to support conversion between markdown and ProseMirror (Tiptap’s internal JSON representation).
  • Register the extension on the editor.

Persistence

JSON schema

Tiptap's persistence model is built on ProseMirror's document schema, a structured JSON representation of the editor's content 8. ProseMirror is a successor to CodeMirror and has been actively maintained for a decade. This approach provides strong guarantees about document validity, composability and extensibility.

Using this schema gives the advantage that we can enforce strict rules about the content layout to provide page templates with restrictions on which content can be edited or how the layout can be composed 9. Each document type can have its own schema that explicitly declares which node types are permitted, in what order, and under what parent relationships. This opens the door to templated editing and validation, where we can lock down layout regions (e.g. header, body, sidebar) or mark specific nodes as read-only.

Markdown

We have the option of serializing to Markdown which may be useful for distribution for more lightweight rendering environments that don't run React or Tiptap directly. Markdown offers a compact, human-readable representation of the content that can be easily rendered to HTML by static site generators, documentation tools, or CMS platforms with minimal dependencies.

Plain HTML

We can also export the editor content as plain HTML for use in legacy renderers 10 or environments with limited developer capacity. This allows older systems to display the content without modification, using standard browser rendering. However, Tiptap's HTML output contains only semantic tags and no styling. Stylesheets must be provided separately or CSS can be inlined within each extension for portability, though that approach can become cumbersome for development.

Display

Displaying content in Learn or projects that already have the editor loaded is simple - the editor can be set to display mode with the editable property set to false 11.

A Static Renderer utility is provided for environments that only need to display content 12. This takes the serialized JSON and renders it out to HTML, markdown or React components.

For wider distribution, we can provide a lightweight library utility for displaying Tiptap content. A minimal setup in Smoot Design that uses the Static Renderer and the StarterKit for common extensions (no React dependency) produced a bundle size of 421kB (116kb gzipped), which is reasonably small. This enables projects running plain HTML/JS or running on a CMS to import with a single script tag.

We can also provide a full editor as a library bundle. A basic instance bundles to 884kB (227kB gzipped). Example code that adds a Tiptap viewer and editor as bundle code to Smoot Design is here: https://github.com/mitodl/smoot-design/compare/jk/tiptap

Accessibility:

Tiptap provides semantic output, but accessibility still depends on how we structure nodes, define heading hierarchies, and manage focus states in custom components and in the general edited content. Ongoing attention will need to be maintained to ensure authored content remains accessible.

Licensing and cost

Tiptap's open-source core is sufficient for most needs. Some templates and more advanced extensions are only available paid subscription 1314. Tiptap offers a hosted platform, however we already have the necessary APIs and infrastructure, so this would not be a requirement.

Comparing CKEditor

  • Headless architecture: Tiptap is headless and framework-native, letting us build or customize our own UI, while CKEditor ships as a prebuilt interface with limited structural control.
  • React integration: Tiptap deeply integrates with React, allowing custom components as nodes. CKEditor's React wrapper is more isolated and DOM-based.
  • Schema control: Tiptap (via ProseMirror) gives full control over content schema and structure. CKEditor's schema is more rigid and harder to customize.
  • Extension model: Tiptap uses modular, composable TypeScript extensions. CKEditor relies on a heavier plugin system with less flexibility.
  • Output formats: Tiptap supports JSON, Markdown, and HTML. CKEditor primarily outputs HTML.
  • Developer focus: Tiptap is a flexible toolkit for developers. CKEditor is a turnkey product focused on ready to use authoring.

Recommended Next Steps

  1. Encapsulate editor setup
  • Create reusable <TiptapEditor /> and <TiptapDisplay /> components in our Smoot Design package.
  • Include editor initialization and common extensions (StarterKit, Markdown, our custom node types).
  • Encapsulate the boilerplate introduced by the Simple Editor template.
  1. Develop a component-extension bridge utility
  • Abstract the pattern for exposing React components as TipTap extensions.
  • Include serialization to Markdown/HTML and deserialization from tokens.
  • This will reduce friction for building future content components that can appear standalone for use in our applications while being quickly available in the editor.
  1. Refine persistence format decision
  • Continue evaluating Markdown (for portability and human readability) vs. ProseMirror JSON (for structured layouts).
  • Consider storing both if migration flexibility is important.
  1. Legacy and display support (optional, if needed)
  • Implement the Static Renderer utility as a lightweight standalone bundle script to support non-React consumers. We have similar setup in Smoot Design for AiChat consumption in the OpenEdX plugin.
  1. Milestones
  • Current: Edit new article page and display articles in Learn. Resurface existing article APIs used on the original article pages.
  • Next: Custom extension API for integrating article components.
  • Next: Design and development cycles for page templates and composable elements. Schema validation mechanism to enforce that pages are built to template / within any constraints.
  • Next: Accessibility review of edited content.

Footnotes

  1. https://tiptap.dev/docs/editor/getting-started/install/reacttage that

  2. https://tiptap.dev/docs/editor/getting-started/install/vanilla-javascript

  3. https://tiptap.dev/docs/editor/extensions/functionality

  4. https://tiptap.dev/docs/editor/extensions/functionality/starterkit

  5. https://tiptap.dev/docs/ui-components/templates/simple-editor

  6. https://tiptap.dev/docs/editor/extensions/custom-extensions/create-new

  7. https://tiptap.dev/docs/editor/markdown/advanced-usage/custom-tokenizer

  8. https://tiptap.dev/docs/editor/core-concepts/schema

  9. https://tiptap.dev/docs/examples/advanced/forced-content-structure

  10. https://tiptap.dev/docs/editor/api/utilities/html

  11. https://tiptap.dev/docs/editor/api/editor#editable

  12. https://tiptap.dev/docs/editor/api/utilities/static-renderer

  13. https://tiptap.dev/docs/editor/extensions/overview?filter=start

  14. https://tiptap.dev/docs/editor/extensions/overview?filter=team

@jonkafton jonkafton marked this pull request as draft October 31, 2025 21:35
@jonkafton jonkafton changed the title Editor test page Tiptap Editor POC Oct 31, 2025
@ChristopherChudzicki
Copy link
Contributor

My thoughts vs ckeditor:

@jonkafton I was not really expecting this proof-of-concept PR to have 148 changed files. Can you comment on that a bit? Is this mostly a tiptap template that we then modify? What's that workflow like?

@jonkafton
Copy link
Contributor Author

I was not really expecting this proof-of-concept PR to have 148 changed files. Can you comment on that a bit? Is this mostly a tiptap template that we then modify? What's that workflow like?

Tiptap supplies an init script https://tiptap.dev/docs/ui-components/install/next that prompts to install one of the two available templates. The files are all the buttons and editor chrome and their styles that are added to the project (see https://tiptap.dev/docs/ui-components/templates/simple-editor). The core install does not eject library code and just provides a contenteditable div with no buttons/menus etc.

We may choose not to use a template. There are extensions available that provide basic controls - https://tiptap.dev/docs/editor/extensions/functionality/starterkit.

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

OpenAPI Changes

Show/hide 3 changes: 0 error, 0 warning, 3 info
3 changes: 0 error, 0 warning, 3 info
info	[request-parameter-property-type-generalized] at head/openapi/specs/v1.yaml	
	in API GET /api/v1/contentfiles/
		for the 'query' request parameter 'resource_id', the type/format of property '/items/' was generalized from 'integer'/'' to 'number'/''

info	[request-parameter-property-type-generalized] at head/openapi/specs/v1.yaml	
	in API GET /api/v1/courses/{learning_resource_id}/contentfiles/
		for the 'query' request parameter 'resource_id', the type/format of property '/items/' was generalized from 'integer'/'' to 'number'/''

info	[request-parameter-property-type-generalized] at head/openapi/specs/v1.yaml	
	in API GET /api/v1/learning_resources/{learning_resource_id}/contentfiles/
		for the 'query' request parameter 'resource_id', the type/format of property '/items/' was generalized from 'integer'/'' to 'number'/''


Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@jonkafton jonkafton mentioned this pull request Nov 10, 2025
@jonkafton
Copy link
Contributor Author

Closing as POC code not intended for merge. The Tiptap editor has been added in this PR:

@jonkafton jonkafton closed this Nov 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants