React Components
The LikeC4 React library is available to embed diagrams into your applications.
Although you can use it directly, consider Vite Plugin
or CLI for smoother developer experience.
You must have react and react-dom installed.
Add @likec4/core and @likec4/diagram:
npm i @likec4/core @likec4/diagrampnpm add @likec4/core @likec4/diagramyarn add @likec4/core @likec4/diagrambun add @likec4/core @likec4/diagramLikeC4 React library can be used in two ways.
Bundled
Section titled “Bundled”This is the easiest way to use the library.
The diagram renders inside the shadow DOM, includes all dependencies, and handles styling.
LikeC4ModelProvider
Section titled “LikeC4ModelProvider”The diagram requires an instance of LikeC4Model.Layouted to render.
You need to prepare it and wrap your diagram with the LikeC4ModelProvider 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.tsThen:
import { LikeC4ModelProvider } from '@likec4/diagram/bundle'// import model from generated fileimport { likec4model } from './likec4-model.ts'
function App() { return ( <LikeC4ModelProvider model={likec4model}> {/* ... */} </LikeC4ModelProvider> )}It’s possible to prepare the model from a 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 the model with the Builder, then lay it out 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()
// The Builder returns a computed model. To render it, you need to lay it outconst 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 exploration in the popup browser.
This component works in most use cases, but if you need more functionality, 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 the package as a library with your bundler, you need to manage the CSS yourself.
The library uses Mantine. If you already use it and have MantineProvider in scope, the LikeC4 diagram 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 the 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 Variableby default.
You can bundle it, import it from fontsource, from any other CDN, or use:@import '@likec4/diagram/styles-font.css'You can override the font, this is explained later.
With PandaCSS
Section titled “With PandaCSS”Check the PandaCSS docs for full setup instructions.
LikeC4 provides a preset.
npm i @likec4/stylespnpm add @likec4/stylesyarn add @likec4/stylesbun add @likec4/stylesConfigure 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.
You must provide an 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 node renderers using primitives from @likec4/diagram/custom
(or @likec4/diagram/bundle/custom for the bundled version).
See customNodes.ts 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