Skip to content

Conversation

@tupizz
Copy link

@tupizz tupizz commented Feb 2, 2026

Summary

Adds @superdoc-dev/react - a first-party React wrapper for SuperDoc with proper lifecycle management, SSR safety, and React Strict Mode compatibility.

Closes SD-1716

Key Design Decisions

Decision Choice
Type system Types extracted from superdoc (no duplication)
Ref API Single getInstance() method
Style import @superdoc-dev/react/style.css
React version 16.8+ (uses useRef for ID generation)
SSR behavior Client-only (renders renderLoading if provided, otherwise null)
documentMode Prop changes handled without rebuild

Package Structure

packages/react/
├── src/
│   ├── index.ts              # Exports
│   ├── SuperDocEditor.tsx    # Main component
│   ├── SuperDocEditor.test.tsx
│   ├── types.ts              # Types extracted from superdoc
│   └── utils.ts              # ID generation (internal)
├── style.css                 # Re-exports superdoc/style.css
├── package.json
├── tsconfig.json
├── vite.config.ts
└── vitest.config.ts

Usage

import { SuperDocEditor } from '@superdoc-dev/react';
import '@superdoc-dev/react/style.css';

function App() {
  const [mode, setMode] = useState<DocumentMode>('editing');
  const editorRef = useRef<SuperDocRef>(null);
  
  return (
    <>
      <button onClick={() => setMode('viewing')}>View</button>
      <SuperDocEditor
        ref={editorRef}
        document={file}
        documentMode={mode}
        onReady={({ superdoc }) => console.log('Ready!')}
      />
    </>
  );
}

// Access instance via ref for methods
await editorRef.current?.getInstance()?.export({ triggerDownload: true });

Props

All props from SuperDocConfig are supported, plus React-specific props:

Prop Type Default Description
document File | Blob | string | object - Document to load
documentMode 'editing' | 'viewing' | 'suggesting' 'editing' Editing mode
role 'editor' | 'viewer' | 'suggester' 'editor' User role
id string auto-generated Custom container ID
user { name, email?, image? } - Current user
users Array<{ name, email, image? }> - All users
modules object - Modules config
hideToolbar boolean false Hide the toolbar
renderLoading () => ReactNode - Loading UI (shown during SSR and init)
className string - Wrapper CSS class
style CSSProperties - Wrapper inline styles

Props That Trigger Rebuild

Prop Reason
document New document to load
user User identity changed
users Users list changed
modules Module configuration changed
role Permission level changed
hideToolbar Toolbar visibility changed

Other props like documentMode and callbacks are handled efficiently without rebuild.

Ref API

interface SuperDocRef {
  getInstance(): SuperDocInstance | null;
}

Examples

Example Description
examples/getting-started/react React + TypeScript example with full type safety
examples/integrations/nextjs-ssr Next.js App Router with SSR support

Run examples from repo root:

pnpm -C examples/getting-started/react dev
pnpm -C examples/integrations/nextjs-ssr dev

Documentation Updated

  • apps/docs/getting-started/frameworks/react.mdx - Full React integration guide
  • apps/docs/getting-started/frameworks/nextjs.mdx - Next.js integration guide
  • packages/react/README.md - Package README
  • packages/react/CLAUDE.md - Implementation details for AI assistants

Test Plan

  • pnpm --filter @superdoc-dev/react build succeeds
  • pnpm --filter @superdoc-dev/react test passes (14 tests)
  • Example apps run and load documents
  • Types correctly extracted from superdoc
  • Ref API works as expected
  • Docs consistent with implementation

@linear
Copy link

linear bot commented Feb 2, 2026

@tupizz tupizz force-pushed the feat/react-wrapper branch 2 times, most recently from af17227 to 9eec016 Compare February 3, 2026 13:26
Copy link
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good start!

the whole point of the React package is to provide the best DX as possible to (react) developers starting with SuperDoc - so let's make sure we nail the basics and keep it simple.

tupizz added 14 commits February 3, 2026 13:52
Add first-party React wrapper for SuperDoc with component-based API,
proper lifecycle management, SSR safety, and React Strict Mode compatibility.

Package features:
- SuperDocEditor component with TypeScript support
- Full ref API exposing all SuperDoc methods
- Dynamic import for SSR safety
- Loading state via renderLoading prop
- Auto-rebuild on document prop change
- Imperative mode switching via setDocumentMode()

Key decisions:
- superdoc as dependency (not peer) for simpler imports
- CSS re-exported via @superdoc/react/style.css
- React 16.8+ compatible (useRef for ID generation)
- Tests use real superdoc (no mocks)
- Replace manual SuperDoc setup with SuperDocEditor component
- Simplify imports to only use @superdoc/react
- Add props reference table
- Add ref methods reference table
- Update SSR section with automatic handling
- Remove custom hook section (no longer needed)
- Update example links to new repo
This commit addresses layout issues in the React wrapper example where
the Events and HTML Export sidebars would overlap with the editor
content on smaller screen sizes.

Changes:

1. Split-view layout improvements:
   - Changed editor-side from `flex: 1` to `flex: 1 1 400px` with
     min-width: 300px for proper flex basis
   - Changed sidebars from fixed width (280px) to flexible sizing:
     `flex: 0 0 240px` with min-width: 200px and max-width: 300px
   - Added flex-wrap: wrap to allow stacking on narrow screens

2. Mobile/narrow screen support (< 700px):
   - Sidebar stacks below the editor instead of side-by-side
   - Editor gets border-bottom instead of border-right
   - Sidebar gets full width with border-top separator

3. Panel header/controls improvements:
   - Added flex-wrap: wrap to panel-header for wrapping controls
   - Added min-height: fit-content to prevent content clipping
   - Added flex-wrap: wrap to panel-controls for button wrapping

4. Toolbar responsiveness:
   - Made toolbar container horizontally scrollable

5. General responsive adjustments:
   - At 900px: Reduced tab padding, font sizes, and control sizes
   - At 600px: Further reductions for very small screens including
     stacked event items and smaller badges
Documentation improvements following MDXEditor/Remotion patterns:

README.md (root):
- Added React quick start section with code example
- Restructured Basic Usage into React and Vanilla JS sections

packages/react/README.md:
- Complete rewrite with concept-first methodology
- Added Core Concepts section (modes, roles, component lifecycle)
- Expanded Props Reference with categorized tables
- Added Common Patterns section with real-world examples
- Added Framework Integration (Next.js App/Pages Router)
- Added Troubleshooting section

packages/react/CLAUDE.md (new):
- Architecture overview with ASCII diagram
- Quick navigation table for key files
- Implementation details (SSR safety, stable IDs, etc.)
- Props → SuperDoc config mapping
- Common tasks guide

.claude/rules/ (new):
- git-commits.md: Commit message format, no AI attribution rule
- react-wrapper.md: React package development patterns
- documentation.md: README vs CLAUDE.md standards
- coding-standards.md: TypeScript, React, CSS conventions

CLAUDE.md (root):
- Added react package to project structure
- Added React to "Where to Look" table
- Added Framework Wrappers section
- Reference to detailed git rules
React documentation (react.mdx):
- Added Core Concepts section (component lifecycle, modes, roles)
- Added Working with Refs section with method reference table
- Added Common Patterns section (file upload, document switching, etc.)
- Added TypeScript Support section with exported types
- Added Framework Integration section (Next.js App/Pages Router, Vite)
- Added Advanced Features section (collaboration, AI, search)
- Added Troubleshooting section
- Added Requirements table
- Used MDXEditor/Remotion-style structure with Tips, Notes, CardGroups

Next.js documentation (nextjs.mdx):
- Updated to recommend @superdoc/react as primary approach
- Added App Router and Pages Router examples
- Added Complete Example with file upload and export
- Added API Route examples for both routers
- Added Authentication example with next-auth
- Added CSS import guidance
- Moved vanilla JS approach to Alternative section
- Added CardGroup navigation to related docs
- Extract types from superdoc constructor (no duplication)
- Simplify ref API to just getInstance()
- Replace tsup with Vite for building
- Add hideToolbar prop (replaces boolean toolbar)
- Clean up unused code and simplify docs
- Fix React version mismatch (19.2.4)
- Add examples/* to pnpm workspace

BREAKING CHANGE: toolbar={false} replaced with hideToolbar prop
- Fix ref API in nextjs.mdx (add missing getInstance() calls)
- Fix installation instructions in README (superdoc is a dependency)
- Remove dead .claude/rules reference from CLAUDE.md
- Fix example to use imperative mode switching (not prop-based)
- Remove undocumented generateId export from public API
- Add clarifying comment about dependency array behavior
The component handles documentMode prop changes efficiently by calling
setDocumentMode() internally - no full rebuild occurs. Updated docs
and example to show the simpler prop-based pattern.
The component now returns null (or renderLoading) on server and only
renders after client-side hydration. This is the correct approach since
SuperDoc requires browser APIs.

- Added isClient state with useEffect to detect client
- Return loading/null on server, full component on client
- Fixed misleading comment in utils.ts about SSR
- Document optional `id` prop in README and react.mdx
- Clarify component is client-only, not SSR-compatible
- Update Tip to explain prop-based documentMode changes work without rebuild
- Change nextjs.mdx example to use prop-based mode switching
- Add "Props That Trigger Rebuild" section
- Fix "bundled" wording to "included"
- Update react example to use SuperDocEditor component
- Remove manual lifecycle management (DocumentEditor.jsx)
- Rename typescript example to react-typescript
- Move react-wrapper example to getting-started/react-typescript
@tupizz tupizz force-pushed the feat/react-wrapper branch from 809d288 to 7c333a6 Compare February 3, 2026 16:53
… main

These files were accidentally modified during the react-wrapper work.
New TypeScript example demonstrating @superdoc/react integration:
- Proper .tsx files with full type safety
- SuperDocRef and DocumentMode types usage
- Typed event handlers and callbacks
- Mode switching (editing/suggesting/viewing)
- Export and getHTML via ref API
- Loading states with renderLoading
- User information
@tupizz tupizz marked this pull request as ready for review February 3, 2026 17:29
Copilot AI review requested due to automatic review settings February 3, 2026 17:29
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cba407d124

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@superdoc-bot
Copy link

superdoc-bot bot commented Feb 4, 2026

🎉 This PR is included in @superdoc-dev/react v1.0.0-canary.5

The release is available on GitHub release

…examples

- Update CI workflow filters to use @superdoc-dev/react
- Update style.css comment to reflect new package name
- Consolidate react-with-typescript example into react example
- Convert react example from JSX to TypeScript
@superdoc-bot
Copy link

superdoc-bot bot commented Feb 4, 2026

🎉 This PR is included in @superdoc-dev/react v1.0.0-canary.6

The release is available on GitHub release

- Replace fixed 100ms delay with waitFor predicate in test for more reliable CI
- Document which props trigger rebuilds vs initial-only in README
- Note: StrictMode is already enabled in the example (main.tsx)
@superdoc-bot
Copy link

superdoc-bot bot commented Feb 4, 2026

🎉 This PR is included in @superdoc-dev/react v1.0.0-canary.7

The release is available on GitHub release

@tupizz tupizz force-pushed the feat/react-wrapper branch from 90d7b76 to 3b9891b Compare February 4, 2026 12:30
- Remove temporary feat/react-wrapper branch from release config
- Fix example links to point to correct directories (react, nextjs-ssr)
- Add Next.js example link to documentation
- Document hideToolbar as rebuild trigger
- Fix README path in example
- Use proper TypeScript event types instead of any
- Add error state UI when initialization fails
- Add containerId/toolbarId to effect dependencies
- Define CallbacksType to reduce code duplication
Demonstrates @superdoc-dev/react integration with Next.js App Router:
- Client-side dynamic import with ssr: false
- TypeScript support
- Basic editor setup
@tupizz tupizz changed the title feat(react): add @superdoc/react wrapper package feat(react): add @superdoc-dev/react wrapper package Feb 4, 2026
@tupizz tupizz self-assigned this Feb 4, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 55 out of 68 changed files in this pull request and generated 5 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +147 to +150
// Capture the current documentMode for this effect run
const effectDocumentMode = initialDocumentModeRef.current;
initialDocumentModeRef.current = documentMode;

Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

effectDocumentMode is taken from initialDocumentModeRef.current, but that ref is only updated inside this rebuild effect (which does not depend on documentMode). If documentMode changes without a rebuild and later a rebuild is triggered (e.g., document changes), the new SuperDoc instance can be initialized with a stale mode. Use the current documentMode value for this effect run (or update the ref in a separate useEffect that depends on documentMode).

Copilot uses AI. Check for mistakes.
Comment on lines +164 to +170
// Build configuration - pass through all props
const superdocConfig = {
...restProps,
selector: `#${CSS.escape(containerId)}`,
// Use internal toolbar container unless hideToolbar is true
...(!hideToolbar && toolbarContainerRef.current ? { toolbar: `#${CSS.escape(toolbarId)}` } : {}),
documentMode: effectDocumentMode,
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrapper always injects its own toolbar selector when hideToolbar is false, which will override a user-provided toolbar prop even though SuperDocConfig supports custom toolbars. Either (1) only set toolbar when the caller did not provide one, or (2) omit toolbar from the exposed props / document that toolbar is managed internally.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
/**
* @superdoc-dev/react - Official React wrapper for SuperDoc
* @packageDocumentation
* @version 1.0.0
*/
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @version 1.0.0 tag in the package documentation header doesn’t match package.json version (0.1.0). This can confuse users and tooling that surfaces TSDoc headers; update the tag to match the package version or remove it.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +15
"react": "19.2.3",
"react-dom": "19.2.3"
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example pins react/react-dom to 19.2.3 while the workspace catalog was bumped to 19.2.4, which can lead to multiple React copies in the monorepo install and confusing hook/type issues. Prefer aligning to the workspace version (e.g., catalog: or the same 19.2.4 range) unless there’s a specific reason to stay on 19.2.3.

Suggested change
"react": "19.2.3",
"react-dom": "19.2.3"
"react": "workspace:*",
"react-dom": "workspace:*"

Copilot uses AI. Check for mistakes.
Comment on lines +275 to +277
const handleReady = ({ superdoc }: { superdoc: any }) => {
console.log('SuperDoc ready');
};
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the TypeScript example, handleReady uses { superdoc: any }, which undermines the “full TypeScript support” claim. Consider using the exported SuperDocReadyEvent (or SuperDocInstance) type for the callback parameter instead of any.

Copilot uses AI. Check for mistakes.
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.

4 participants