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:react
resolve 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
/#saas
open thesaas
view. - Back/forward navigation: browser history works naturally with hash changes.
This project is provided as an educational example for LikeC4 customization.