This repository demonstrates advanced customization capabilities of LikeC4.
The main purpose of this example is to showcase how to extend and customize LikeC4's default behavior through:
- Custom Element Nodes: Override default node rendering with custom React components
- Custom Overlays: Create interactive dialogs for displaying detailed information
- Custom Shapes: Implement unique visual representations using SVG and drawing libraries
- Enhanced Interactions: Add custom buttons, toolbars, and interactive behaviors
- Style Customization: Override default theme and inject custom CSS
Open this repository with:
- StackBlitz: Open in StackBlitz
- CodeSandbox: Open in CodeSandbox
- Gitpod: Open in Gitpod
View the architecture model in the LikeC4 DSL source file.
This project created by npx create-vite --template react-ts.
It uses the LikeC4 Vite plugin to load and compile the .c4 model files.
import {
DefaultHandles,
ElementActions,
ElementData,
ElementNodeContainer,
XYFlow,
elementNode,
useDiagram,
} from 'likec4/react'
// Custom rendering of logical elements:
// - adds extra action button
// - displays toolbar with metadata on hover
export const ElementNode = elementNode(({ nodeModel, nodeProps }) => {
const diagram = useDiagram()
return (
<ElementNodeContainer nodeProps={nodeProps}>
{/* Use custom shape, see below */}
<CustomShape nodeModel={nodeModel} nodeProps={nodeProps} />
<ElementData {...nodeProps} />
<ElementActions {...nodeProps}
extraButtons={[{
key: 'details',
icon: <PlusIcon />,
onClick(e) {
e.stopPropagation();
open(nodeModel.id);
},
}]}
/>
<DefaultHandles />
{/* Custom toolbar with metadata */}
{nodeProps.data.hovered
&& nodeModel.element.hasMetadata()
&& (
<XYFlow.NodeToolbar>
{entries(nodeModel.element.getMetadata())
.map(([key, value]) => (
<MetadataValue key={key} label={key} value={value} />
))}
</XYFlow.NodeToolbar>
)
}
</ElementNodeContainer>
);
})- Render anything on top of default shape: This example displays Stripe logo based on metadata
- Hand-drawn Rectangles: Uses RoughJS to create organic, sketch-like rectangles
- Fallback to default shape: If no custom shape is found, the default shape is rendered
import type { ElementNodeProps } from 'likec4/react'
import { ElementShape } from 'likec4/react'
export function CustomShape({nodeModel, nodeProps}: ElementNodeProps) {
const metadata = nodeModel.element.getMetadata()
const nodeData = nodeProps.data
switch(true) {
case metadata._shape === 'stripe': {
return <>
<ElementShape {...nodeProps}/>
<StripeLogo/>
</>
}
case nodeData.shape === 'rectangle': {
return <RoughRectangle width={nodeData.width} height={nodeData.height}/>
}
default: {
return <ElementShape {...nodeProps}/>
}
}
}LikeC4 provides a PortalToContainer that can be used to render your components in the diagram's Shadow DOM.
import {
Overlay,
PortalToContainer,
useLikeC4Model,
} from 'likec4/react'
export function CustomOverlay({ elementId, close }: Props) {
const likec4model = useLikeC4Model()
const element = likec4model.findElement(elementId)
return (
<PortalToContainer>
{element && (
<Overlay onClose={close}>
<h1>Custom Overlay</h1>
<pre>
<code>{JSON.stringify(element.$element, null, 2)}</code>
</pre>
</Overlay>
)}
</PortalToContainer>
)
}You can override default colors and styles globally using likec4/likec4.config.ts.
import { defineConfig } from 'likec4/config'
export default defineConfig({
name: 'example',
styles: {
theme: {
colors: {
// This will override the default primary color
primary: '#256828',
}
},
defaults: {
color: 'sky', // Set default color for nodes,
// replacing "primary"
opacity: 10,
relationship: {
line: 'solid',
arrow: 'open'
}
}
}
})- Node.js 20+
- pnpm (recommended) or npm
-
Install dependencies:
pnpm install
The Vite plugin is automatically enabled via your project configuration -
vite.config.ts. -
Start development server:
pnpm dev
-
Open your browser: Navigate to
http://localhost:5173 -
Reference types (already set up):
Insrc/vite-env.d.ts, we include: TypeScript ambient types are hooked up viasrc/vite-env.d.ts./// <reference types="likec4/vite-plugin-modules" />This augments TypeScript with module declarations exposed by the LikeC4 Vite plugin so imports like
likec4:reactresolve with proper types.
Routing between views is handled via the URL hash. The app reads the hash to decide which LikeC4 view to display and updates it on navigation:
// src/App.tsx
const [hash, setHash] = useHash()
const viewId = hash.slice(1) || 'index'
<ReactLikeC4
viewId={viewId}
onNavigateTo={(id) => setHash(`#${id}`)}
...
/>- Deep-linking: shareable URLs like
/#saasopen thesaasview. - Back/forward navigation: browser history works naturally with hash changes.
This project is provided as an educational example for LikeC4 customization.