
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
react-embed-docs
Advanced tools
Full-stack documentation system with BlockNote editor, hierarchical structure, and file uploads
Full-stack documentation system with BlockNote editor, hierarchical structure, and file uploads.
# Using bun
bun install react-embed-docs
# Using npm
npm install react-embed-docs
# Using yarn
yarn add react-embed-docs
{
"react": "^18.0.0",
"react-dom": "^18.0.0",
"@blocknote/core": "^0.46.0",
"@blocknote/react": "^0.46.0",
"@blocknote/mantine": "^0.46.0",
"drizzle-orm": "^0.31.0",
"@tanstack/react-query": "^5.0.0",
"postgres": "^3.4.0"
}
Run the migrations to create the necessary tables:
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import { runMigrations } from 'react-embed-docs/server'
const connection = postgres(process.env.DATABASE_URL)
const db = drizzle(connection)
// Run migrations on startup
await runMigrations(db)
import { Hono } from 'hono'
import { createDocsRouter, createFilesRouter } from 'react-embed-docs/server'
const app = new Hono()
// Add docs routes
app.route('/api/docs', createDocsRouter({ db }))
app.route('/api/files', createFilesRouter({ db }))
// Or combine both
import { createRouter } from 'react-embed-docs/server'
app.route('/api', createRouter({ db }))
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { DocsLayout, DocumentEdit } from 'react-embed-docs/client'
import 'react-embed-docs/styles'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<DocsLayout
onNavigate={(id) => window.location.href = `/docs/${id}`}
userAvatar={<UserAvatar />} // Your custom avatar component
>
<DocumentEdit
docId="new"
onSave={(doc) => console.log('Saved:', doc)}
onNavigate={(id) => window.location.href = `/docs/${id}`}
/>
</DocsLayout>
</QueryClientProvider>
)
}
import {
DocsService,
FilesService,
createDocsRouter,
createFilesRouter,
createRouter,
runMigrations,
documentsTable,
filesTable
} from 'react-embed-docs/server'
class DocsService {
// Queries
list(filters, options): Promise<ListDocumentsResult>
getById(id: string): Promise<Document | undefined>
getBySlug(slug: string): Promise<Document | undefined>
getChildren(parentId: string): Promise<Document[]>
getTree(): Promise<Document[]>
// Mutations
create(data: InsertDocument): Promise<Document>
update(id: string, data: UpdateDocument): Promise<Document | undefined>
delete(id: string): Promise<Document | undefined>
// Search
search(query: string, options): Promise<SearchResult[]>
searchText(query: string, options): Promise<SearchTextResult[]>
// Reordering
reorder(id: string, newParentId: string | null, newOrder: number): Promise<Document | undefined>
}
import {
DocsLayout,
DocumentEdit,
DocumentView,
DocumentList,
EmojiPicker,
useDocumentsQuery,
useDocumentQuery,
useCreateDocumentMutation,
useUpdateDocumentMutation,
useDeleteDocumentMutation,
useReorderDocumentMutation,
useFileUpload
} from 'react-embed-docs/client'
DocsLayout
<DocsLayout
currentDocId?: string // Currently active document ID
onNavigate?: (id: string) => void // Navigation callback
userAvatar?: React.ReactNode // User avatar component for header
onSearch?: (query: string) => void // Search callback
>
{children}
</DocsLayout>
DocumentEdit
<DocumentEdit
docId: string // 'new' or document ID
parentId?: string // Parent document ID (for creating child)
onSave?: (doc: Document) => void
onNavigate?: (id: string) => void
/>
DocumentView
<DocumentView
docId: string
onEdit?: (id: string) => void
onCreateChild?: (parentId: string) => void
onNavigate?: (id: string) => void
/>
// Queries
const { data, isLoading } = useDocumentsQuery({ search?: string, parentId?: string })
const { data } = useDocumentQuery(docId)
// Mutations
const create = useCreateDocumentMutation()
const update = useUpdateDocumentMutation()
const delete = useDeleteDocumentMutation()
const reorder = useReorderDocumentMutation()
// File upload
const { isUploading, progress, error, uploadFile } = useFileUpload({
maxSize: 5 * 1024 * 1024, // 5MB
allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
})
| Column | Type | Description |
|---|---|---|
| id | varchar(21) | Primary key (nanoid) |
| title | varchar(255) | Document title |
| slug | varchar(255) | URL-friendly unique slug |
| content | jsonb | BlockNote JSON content |
| searchIndex | text | Pre-built search text (title + content) |
| emoji | varchar(10) | Document icon emoji |
| cover | varchar(500) | Cover image file ID |
| isPublished | boolean | Published state |
| parentId | varchar(21) | Parent document ID (self-reference) |
| order | integer | Sort order within parent |
| authorId | integer | Document author (optional) |
| createdAt | timestamp | Creation time |
| updatedAt | timestamp | Last update time |
| deletedAt | timestamp | Soft delete timestamp |
| Column | Type | Description |
|---|---|---|
| id | varchar(21) | Primary key (nanoid) |
| filename | varchar(255) | Original filename |
| mimeType | varchar(100) | File MIME type |
| size | integer | File size in bytes |
| content | text | Base64 encoded file content |
| createdAt | timestamp | Upload time |
The package includes a complete Tailwind CSS stylesheet with all utilities. Import it in your app:
import 'react-embed-docs/styles'
This imports a pre-built CSS bundle that includes all Tailwind utilities used by the components. No additional Tailwind configuration needed.
If you prefer to process the styles through your own Tailwind build (e.g., for custom theming), use the source CSS instead:
import 'react-embed-docs/styles/source'
Then add the package files to your Tailwind content config:
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/react-embed-docs/dist/client/**/*.js',
],
}
If you're using the source CSS and some classes aren't working, you can use the safelist:
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx}',
// Add this to scan the safelist
'./node_modules/react-embed-docs/styles/tailwind.safelist.js',
],
safelist: [
// Option 1: Include all classes from the package
...require('react-embed-docs/styles/tailwind.safelist.js'),
// Option 2: Or manually add specific classes you need
'h-40', 'h-44', 'h-48', 'h-52', 'h-56', 'h-60',
],
}
All components support dark mode through Tailwind's dark: variants. The components automatically adapt when you have class="dark" on your HTML element.
The components are fully responsive and mobile-friendly. For optimal mobile experience, ensure your HTML includes the viewport meta tag:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
</head>
Mobile features:
app.route('/custom-prefix', createDocsRouter({ db }))
const { uploadFile } = useFileUpload({
maxSize: 10 * 1024 * 1024, // 10MB
allowedTypes: ['image/jpeg', 'image/png', 'image/webp', 'application/pdf']
})
The package doesn't enforce authentication, allowing you to integrate with your own auth system:
// Add your auth middleware before docs routes
app.use('/api/docs/*', yourAuthMiddleware)
app.route('/api/docs', createDocsRouter({ db }))
const createMutation = useCreateDocumentMutation()
const handleCreate = async () => {
const doc = await createMutation.mutateAsync({
title: 'My New Document',
slug: 'my-new-document',
content: [], // BlockNote blocks
emoji: '📝',
isPublished: true
})
// Navigate to the new document
router.push(`/docs/${doc.id}`)
}
const { uploadFile } = useFileUpload()
const updateMutation = useUpdateDocumentMutation()
const handleCoverUpload = async (file: File) => {
const result = await uploadFile(file)
if (result) {
await updateMutation.mutateAsync({
id: docId,
cover: result.url // /api/files/{id}
})
}
}
const [searchQuery, setSearchQuery] = useState('')
const { data } = useDocumentsQuery({ search: searchQuery })
// Results include documents matching title or content
If you're migrating from an existing docs implementation:
searchIndex column for faster searchreact-embed-docs/clientreact-embed-docs/serverreact-embed-docs/styles or copy CSS classes# Install dependencies
bun install
# Build
bun run build
# Watch mode
bun run dev
# Run migrations
bun run migrate
MIT License - see LICENSE file for details.
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
FAQs
Full-stack documentation system with BlockNote editor, hierarchical structure, and file uploads
We found that react-embed-docs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.