React Components
LikeC4 React library is available to embed diagrams into your applications.
Although you can use it directly, consider Vite Plugin
or CLI for smoother development experience.
You must have react
and react-dom
installed.
Add @likec4/core
and @likec4/diagram
:
npm i @likec4/core @likec4/diagram
pnpm add @likec4/core @likec4/diagram
yarn add @likec4/core @likec4/diagram
bun add @likec4/core @likec4/diagram
LikeC4 React library can be used in two ways.
Bundled
Section titled “Bundled”This is the easiest way to use the library.
Diagram renders inside shadow DOM, already includes all the dependencies and takes care of the styling.
LikeC4ModelProvider
Section titled “LikeC4ModelProvider”Diagram requires instance of LikeC4Model.Layouted
to render.
You need to prepare it and wrap your diagram withLikeC4ModelProvider
component.
Below are examples of how to prepare the model:
- Using CLI codegen
- Using Source files
- Using Model Builder
Prepare model with code generation:
likec4 codegen model --outfile ./likec4-model.ts
Then:
import { LikeC4ModelProvider } from '@likec4/diagram/bundle'// import model from generated fileimport { likec4model } from './likec4-model.ts'
function App() { return ( <LikeC4ModelProvider model={likec4model}> {/* ... */} </LikeC4ModelProvider> )}
It is possible to prepare model from string. See API usage:
import { LikeC4 } from 'likec4'import { LikeC4ModelProvider } from 'likec4/react'
const likec4 = await LikeC4.fromWorkspace('/path/to/workspace')const likec4model = await likec4.layoutedModel()
function App() { return ( <LikeC4ModelProvider model={likec4model}> {/* ... */} </LikeC4ModelProvider> )}
You can prepare model with Builder, then layout it with layoutLikeC4Model
:
import { LikeC4ModelProvider } from '@likec4/diagram/bundle'import { Builder } from "@likec4/core/builder"import { layoutLikeC4Model } from "@likec4/layouts"
const computedModel = Builder .specification({46 collapsed lines
elements: { actor: { style: { shape: 'person', }, }, system: {}, component: {}, }, relationships: { likes: {}, }, tags: ['tag1', 'tag2', 'tag1'], }) .model(({ actor, system, component, relTo, rel }, _) => _( actor('alice'), actor('bob'), rel('alice', 'bob', { tags: ['tag1'], // you get code completion for tags kind: 'likes', // code completion for kind }), system('cloud', { tags: ['tag1', 'tag2'] }).with( component('backend').with( component('api'), component('db'), // code completion for relationships rel('cloud.backend.api', 'cloud.backend.db') ), component('frontend').with( relTo('cloud.backend.api') ), ), ) ) .views(({ view, viewOf, $include, $style }, _) => _( view('index', 'Index').with( $include('cloud.*'), ), viewOf('ui', 'cloud.ui').with( $include('* -> cloud.**'), $style('cloud.ui', { color: 'red' }), ), ) ) .toLikeC4Model()
// Builder returns computed model, and to render it you need to layout itconst likec4model = await layoutLikeC4Model(computedModel)
function App() { return ( <LikeC4ModelProvider model={likec4model}> {/* ... */} </LikeC4ModelProvider> )}
LikeC4View
Section titled “LikeC4View”import { LikeC4View, LikeC4ModelProvider } from '@likec4/diagram/bundle'
function App() { return ( <LikeC4ModelProvider model={likec4model}> <LikeC4View viewId="index1" onNodeClick={(nodeId) => console.log(nodeId)} /> {/* Possible to have multiple views */} <LikeC4View viewId="index2" /> </LikeC4ModelProvider> )}
See LikeC4ViewProps for available props.
ReactLikeC4
Section titled “ReactLikeC4”LikeC4View
renders views from your model, and allows exploring in the popup browser.
Component works in most use-cases, but if you need more - use ReactLikeC4
:
import { ReactLikeC4, LikeC4ModelProvider } from '@likec4/diagram/bundle'
function App() { const [viewId, setViewId] = useState('index') return ( <LikeC4ModelProvider model={likec4model}> <ReactLikeC4 viewId={viewId} pannable zoomable={false} keepAspectRatio showNavigationButtons enableDynamicViewWalkthrough={false} enableElementDetails enableRelationshipDetails showDiagramTitle={false} onNavigateTo={setViewId} onNodeClick={...} /> </LikeC4ModelProvider> )}
Available hooks inside LikeC4View
or ReactLikeC4
:
import { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel, useEnabledFeatures, useCurrentViewId,
// XYFlow hooks useXYFlow, useXYStore, useXYStoreApi,
// Diagram API useDiagram,
// Select from state useDiagramContext} from '@likec4/diagram/bundle'
If you use built-in icons, install @likec4/icons
(or use likec4/icons
):
import type { ElementIconRenderer } from '@likec4/diagram/bundle'import { LikeC4ModelProvider, LikeC4View, ReactLikeC4 } from '@likec4/diagram/bundle'import { lazy, Suspense } from 'react'
// Better to lazy load icons, bundle is quite large at the momentconst Icon = lazy(async () => { const { IconRenderer } = await import('@likec4/icons/all') return { default: IconRenderer }})
const IconRenderer: ElementIconRenderer = (props) => ( <Suspense> <Icon {...props} /> </Suspense>)
function App() { return ( <LikeC4ModelProvider model={likec4model}> <LikeC4View viewId="index1" renderIcon={IconRenderer} /> {/* Same for ReactLikeC4 */} <ReactLikeC4 viewId="index2" renderIcon={IconRenderer} /> </LikeC4ModelProvider> )}
Library
Section titled “Library”If you want to use package as a library with your bundler, you have to take care of CSS.
Library uses Mantine. If you already use it and have MantineProvider
on the scope - LikeC4 diagramr will use it.
Otherwise, it will wrap itself with MantineProvider
.
Even if you are not using Mantine in your app, its styles are required for the diagrams to work (don’t worry, Mantine is tree-shakable).
Here are the options:
With bundled styles
Section titled “With bundled styles”-
Import all styles
@import '@likec4/diagram/styles.css'This includes all styles, including Mantine styles.
-
If you are using Mantine
@layer reset, base, mantine, xyflow, tokens, recipes, utilities;@import "@mantine/core/styles.layer.css";@import "@likec4/diagram/styles-min.css"; -
Font.
LikeC4Diagram usesIBM Plex Sans
by default.
You can bundle it, or import from fontsource, any other CDN or:@import '@likec4/diagram/styles-font.css'You can override the font, this is explained later.
With PandaCSS
Section titled “With PandaCSS”Check PandaCSS docs for full setup instructions.
LikeC4 provides preset.
npm i @likec4/styles
pnpm add @likec4/styles
yarn add @likec4/styles
bun add @likec4/styles
Configure your panda.config.ts
:
import likec4preset from '@likec4/styles/preset'import { defineConfig } from '@pandacss/dev'
export default defineConfig({ include: [ 'src/**/*.{ts,tsx}', // Include likec4 diagram source code to get the styles './node_modules/@likec4/diagram/panda.buildinfo.json', ], importMap: [ '@likec4/styles', ], presets: [ likec4preset, ], theme: { extend: { // Here you can override/extend the theme }, },})
You global CSS should look like this:
@layer reset, base, mantine, xyflow, tokens, recipes, utilities;@import "@mantine/core/styles.layer.css";@import "@likec4/diagram/styles-xyflow.css";@import "@likec4/diagram/styles-font.css";
Same as ReactLikeC4, but import from @likec4/diagram
and you have to provide instance of DiagramView
:
import { LikeC4Diagram, LikeC4ModelProvider, useLikeC4ViewModel } from '@likec4/diagram'
function LikeC4View({viewId}: {viewId: string}) { const view = useLikeC4ViewModel(viewId) if (!view) { return <>View not found</> } return ( <LikeC4Diagram view={view.$view} readOnly pannable zoomable={false} keepAspectRatio showNavigationButtons enableDynamicViewWalkthrough={false} enableElementDetails enableRelationshipDetails showDiagramTitle={false} /> )}
function App() { return ( <LikeC4ModelProvider model={likec4model}> <LikeC4View viewId="index" /> </LikeC4ModelProvider> )}
Customization
Section titled “Customization”You can render any component inside LikeC4Diagram
(or LikeC4View
/ReactLikeC4
if you are using bundle):
import { LikeC4Diagram, LikeC4ModelProvider } from '@likec4/diagram'import { Panel, ViewportPortal } from '@xyflow/react'
function App() { return ( <LikeC4Diagram> <YourComponent />
{/* You can use components from xyflow */} <Panel position="top"> <p>Your component as a panel</p> <a href="https://reactflow.dev/examples">Check examples</a> </Panel>
<ViewportPortal> <div style={{ transform: 'translate(100px, 100px)', position: 'absolute', }}> This div is positioned at [100, 100] on the diagram canvas </div> </ViewportPortal> </LikeC4Diagram> )}
Custom node renderers
Section titled “Custom node renderers”LikeC4Diagram can use custom node renderers.
Compose custom nodes renderers using primitives from @likec4/diagram/custom
(or @likec4/diagram/bundle/custom
for the bundled version).
See customNodes.tsx for examples.
import { LikeC4Diagram } from '@likec4/diagram'import { ElementActions, ElementDetailsButtonWithHandler, elementNode, ElementNodeContainer, ElementShape, ElementTitle, ElementToolbar, IfNotReadOnly,} from '@likec4/diagram/custom'import { IconPlus } from '@tabler/icons-react'
const customNodes = { element: elementNode(({ nodeProps, nodeModel }) => ( <ElementNodeContainer nodeProps={nodeProps}> <ElementShape {...nodeProps} /> <ElementTitle {...nodeProps} /> {/* Add extra buttons */} <ElementActions {...nodeProps} extraButtons={[ { key: 'plus', icon: <IconPlus />, onClick: () => console.log('extra'), }, ]} /> {/* Add extra info */} <div style={{ position: 'absolute', bottom: 0 }}> {nodeModel.element.getMetadata('your-attr')} </div> </ElementNodeContainer> )),}
function App() { return ( <LikeC4Diagram view={view} renderNodes={customNodes} /> )}
You can also use hooks to access the model and diagram API.
Custom styles
Section titled “Custom styles”LikeC4Diagram uses PandaCSS for styling. You can use it to customize the styles.
TODO: add example