Tiptap rich text editor integration for OpenSaas Stack
npm install @opensaas/stack-tiptapRich text editor integration for OpenSaas Stack using Tiptap.
- ✅ Rich text editing with Tiptap editor
- ✅ JSON storage in database
- ✅ SSR-safe Next.js integration
- ✅ Edit and read-only modes
- ✅ Customizable toolbar and UI options
- ✅ Full TypeScript support
- ✅ Integrates with OpenSaas access control
This package is designed as a separate optional dependency to keep the core stack lightweight.
``bash`
pnpm add @opensaas/stack-tiptap
The following peer dependencies are required:
- @opensaas/stack-core@opensaas/stack-ui
- next
- react
- react-dom
-
1. Register the field component on the client side:
`typescript
// lib/register-fields.ts
'use client'
import { registerFieldComponent } from '@opensaas/stack-ui'
import { TiptapField } from '@opensaas/stack-tiptap'
registerFieldComponent('richText', TiptapField)
`
2. Import the registration in your admin page:
`typescript
// app/admin/[[...admin]]/page.tsx
import { AdminUI } from "@opensaas/stack-ui";
import config from "../../../opensaas.config";
import "../../../lib/register-fields"; // Import to trigger registration
export default async function AdminPage() {
// ... your code
return
}
`
3. Define your schema with the richText field builder:
`typescript
// opensaas.config.ts
import { config, list } from '@opensaas/stack-core'
import { text } from '@opensaas/stack-core/fields'
import { richText } from '@opensaas/stack-tiptap/fields'
export default config({
db: {
provider: 'sqlite',
url: 'file:./dev.db',
},
lists: {
Article: list({
fields: {
title: text({ validation: { isRequired: true } }),
content: richText({
validation: { isRequired: true },
}),
},
}),
},
})
`
4. Generate Prisma schema:
`bash`
pnpm generate
This will create a Prisma field with type Json:
`prisma`
model Article {
id String @id @default(cuid())
title String
content Json // Tiptap JSON content
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
#### Validation
`typescript`
content: richText({
validation: {
isRequired: true, // Make field required
},
})
#### UI Customization
`typescript`
content: richText({
ui: {
placeholder: 'Start writing...',
minHeight: 200, // Minimum editor height in pixels
maxHeight: 800, // Maximum editor height (scrollable)
},
})
Rich text fields work seamlessly with OpenSaas access control:
`typescript`
Article: list({
fields: {
content: richText({
validation: { isRequired: true },
access: {
read: () => true,
create: isSignedIn,
update: isAuthor,
},
}),
},
})
Content is stored as JSON and can be queried using Prisma's JSON operations:
`typescript
import { prisma } from './lib/context'
// Create article with rich text
const article = await prisma.article.create({
data: {
title: 'My Article',
content: {
type: 'doc',
content: [
{
type: 'paragraph',
content: [{ type: 'text', text: 'Hello world!' }],
},
],
},
},
})
// Query articles
const articles = await prisma.article.findMany({
select: {
title: true,
content: true,
},
})
`
The TiptapField component includes:
- Bold
- _Italic_
- ~~Strike-through~~
- H1, H2, H3
- Bullet lists
- Ordered lists
- Quote blocks
- Edit mode: Full toolbar with all formatting options
- Read mode: Render-only view (no toolbar)
Create a custom Tiptap component with additional extensions:
`typescript
// components/CustomTiptapField.tsx
"use client";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Link from "@tiptap/extension-link";
import Image from "@tiptap/extension-image";
export function CustomTiptapField(props) {
const editor = useEditor({
extensions: [
StarterKit,
Link,
Image,
],
content: props.value,
immediatelyRender: false,
onUpdate: ({ editor }) => {
props.onChange(editor.getJSON());
},
});
return
}
`
Then use it in your config:
`typescript
import { registerFieldComponent } from '@opensaas/stack-ui'
import { CustomTiptapField } from './components/CustomTiptapField'
// Global registration
registerFieldComponent('richTextExtended', CustomTiptapField)
// Use in config
fields: {
content: richText({
ui: { fieldType: 'richTextExtended' },
})
}
// Or per-field override
fields: {
content: richText({
ui: { component: CustomTiptapField },
})
}
`
This package follows OpenSaas's extensibility pattern:
1. Field Builder (richText()) - Defines field configurationRichTextField
- Returns typegetZodSchema()
- Implements , getPrismaType(), getTypeScriptType()Json
- Stores data as in Prisma
2. React Component (TiptapField) - UI implementation"use client"
- Client component with directiveimmediatelyRender: false
- SSR-safe with
- Supports edit and read modes
3. No Core Modifications - Extends stack without changes
- Uses BaseFieldConfig extension point
- Compatible with access control system
- Works with hooks and validation
See examples/tiptap-demo for a complete working example demonstrating:
- Multiple rich text fields
- Custom UI options
- Access control integration
- Database operations
Creates a rich text field configuration.
Options:
- validation.isRequired - Make field required (default: false)ui.placeholder
- - Placeholder text (default: "Start writing...")ui.minHeight
- - Minimum editor height in pixels (default: 200)ui.maxHeight
- - Maximum editor height in pixels (default: undefined)ui.component
- - Custom React componentui.fieldType
- - Global field type nameaccess
- - Field-level access control
Returns: RichTextField
React component for rendering the Tiptap editor.
Props:
- name: string - Field namevalue: any
- - JSON content valueonChange: (value: any) => void
- - Change handlerlabel: string
- - Field labelerror?: string
- - Validation error messagedisabled?: boolean
- - Disable editingrequired?: boolean
- - Show required indicatormode?: "read" | "edit"
- - Display modeplaceholder?: string
- - Placeholder textminHeight?: number
- - Minimum heightmaxHeight?: number` - Maximum height
-
Contributions are welcome! This package is part of the OpenSaas Stack monorepo.
MIT