KJDRAW / REACT INTEGRATION
React integration
Mount the complete KJDraw editor as a React component, control it through a typed ref, or build a custom headless integration.
Render the editor component#
import { useRef, useState } from 'react'
import { KJDraw, type KJDrawEditor, type KJWorkbenchLayout } from '@kanjieteam/kjdraw/react'
export function DrawingEditor() {
const editor = useRef<KJDrawEditor | null>(null)
const [layout, setLayout] = useState<KJWorkbenchLayout>('classic')
async function moveSelection() {
const instance = editor.current
const ids = instance?.getSelection() ?? []
if (!instance || !ids.length) return
await instance.execute('MOVE', { ids, dx: 10, dy: 0 })
}
return (
<section>
<button onClick={() => setLayout('focus')}>Focus drawing</button>
<button onClick={() => void moveSelection()}>Move selected</button>
<KJDraw
ref={editor}
document="sample"
locale="en"
theme="dark"
layout={layout}
style={{ width: '100%', height: 720 }}
onReady={instance => instance.fit()}
/>
</section>
)
}
KJDraw mounts the complete editor and gives the ref a KJDrawEditor. Use the ref for open(), save(), execute(), selection and view methods. layout="classic" provides the full CAD ribbon and panels, compact shortens the ribbon, and focus prioritizes the canvas. Changing layout updates the existing editor in place, so its drawing, selection and undo history stay intact. React unmount disposes the editor. See the Editor API for every prop and method.
A minimal hook#
import { useEffect, useRef, useState } from 'react'
import { createKJDrawSDK, type KJDrawSDK } from '@kanjieteam/kjdraw'
export function useKJDraw() {
const sdkRef = useRef<KJDrawSDK | null>(null)
if (!sdkRef.current) {
sdkRef.current = createKJDrawSDK()
sdkRef.current.createDocument({
documentId: 'react-drawing',
units: 'millimeter',
})
}
const sdk = sdkRef.current
const document = sdk.activeDocument
if (!document) throw new Error('KJDraw initialization failed')
const [revision, setRevision] = useState(document.revision)
useEffect(
() => sdk.events.on('command:committed', ({ document: changed }) => {
if (changed === document) setRevision(changed.revision)
}),
[sdk, document],
)
const drawLine = () => sdk.executeCommand('CREATE', {
type: 'LINE',
payload: { start: [0, 0, 0], end: [100, 0, 0] },
}, { document })
return { sdk, document, revision, drawLine }
}
Lifecycle rules#
- Create the SDK once for the editor scope, not during every render.
- Return the event disposer from
useEffect; React will unsubscribe on cleanup. - Store small reactive signals such as revision, selection or active tool. Keep canonical geometry in
KJDocument. - Execute edits through SDK commands so React, plugins and agents share transactions and undo.
Rendering#
The /core headless entry does not impose a React renderer or state library. A custom canvas component can redraw after committed events, while property panels select typed records from the active document. For large drawings, derive viewport-specific display data instead of copying the entire object graph into React state.
Use KJDraw when you need a working CAD surface immediately, or the headless hook when your product owns every interface layer. See the maintained examples for complete applications that use the same public package exports.