C4 Modelizer is a web application that lets you design, explore and document a system architecture using the C4 model. It supports collaborative diagram editing, import/export with schema versioning and a fully‑typed plugin API.
- Visual communication — help teams discuss and refine their architecture with clear diagrams.
- Single source of truth — all diagrams share the same JSON model; no drift between views.
- Developer‑friendly — React + TypeScript + Vite for a fast, hackable codebase.
- Customisable — blocks, icons and behaviours can be extended through the plugin system (see below).
Category | Details |
---|---|
Diagram types | System ↔ Container ↔ Component ↔ Code (C4 levels 1‑4) |
Edit | Drag‑and‑drop nodes, inline rename, technology picker, context menus |
Blocks | Custom icons / colours, technology tags, markdown description |
Relations | Create, edit, style connections and dependencies between any levels |
Import / Export | JSON with embedded schema version; copy‑paste between instances |
UX | Multilingual (i18n), responsive layout, dark theme |
- Vite — lightning‑fast build and HMR
- React + TypeScript
- Material UI — component library
- @xyflow/react — canvas & graph layout
- i18next — internationalisation engine
The quickest way to run C4 Modelizer:
# Pull the image from Docker Hub
$ docker pull eth3rnit3/c4_modelizer:latest
# Run the container
$ docker run -p 8080:80 eth3rnit3/c4_modelizer:latest
Open http://localhost:8080 in your browser.
- Node.js ≥ 22
- npm ≥ 11 (or pnpm ≥ 9 / yarn ≥ 4)
# 1. Clone the repo
$ git clone https://github.com/eth3rnit3/c4_modelizer.git
$ cd c4_modelizer
# 2. Install dependencies
$ npm install # or pnpm install
# 3. Start the dev server
$ npm run dev
Open http://localhost:5173 in your browser.
C4 Modelizer ships with a light but powerful runtime plugin API. You can inject new UI pieces, override existing ones, render global overlays or even access the core Zustand store—all without touching the core source.
┌────────────┐ loadPlugins() ┌───────────────┐
│ Core app │ ───────────────▶ │ Your plugin │
└────────────┘ └───────────────┘
▲ registry.register*() │
└────────────────────────────────┘
- The core exposes a singleton
PluginRegistry
. - At boot,
loadPlugins()
readsVITE_PLUGINS
(comma‑separated package names) and imports each one. - A plugin exports a
C4Plugin
object with asetup(registry)
function where it registers components, portals and methods. - Slots (
ToolbarSlot
,NavBarSlot
, etc.) resolve their component through the registry at render time.
The core registers a helper so plugins can share the exact same Zustand store:
// inside the core (already done)
registry.registerMethod('useStore', () => useC4Store)
In a plugin:
import type { C4Plugin } from 'c4_modelizer/plugin'
const plugin: C4Plugin = {
name: 'stats',
version: '1.0.0',
setup(reg) {
const useStore = reg.getMethod('useStore')!() // the real hook
reg.registerComponent('panel:stats', async () => {
return function StatsPanel() {
const total = useStore((s) => s.model.systems.length)
const reset = useStore((s) => s.actions.reset)
return (
<div>
<p>{total} systems</p>
<button onClick={reset}>Reset</button>
</div>
)
}
})
},
}
export default plugin
getMethod()
returns the factory you registered; calling it yields the hook. All store selectors & actions are thus fully typed.
The core provides a <PortalTarget id="global-overlay" />
at the root of the DOM. Any plugin can push a React node into it:
import Overlay from './Overlay'
registry.registerPortal('global-overlay', <Overlay />)
Overlay.tsx
can be anything—modals, toasts, real‑time collaboration panels—and it’s rendered outside the normal React tree, above everything else.
To update or remove the overlay later:
registry.registerPortal('global-overlay', null) // remove
registry.registerPortal('global-overlay', <NewOverlay />) // replace
Identifier | Purpose | Core default |
---|---|---|
root:provider |
Wrap the whole app | React.Fragment |
toolbar:main |
Main action toolbar | Toolbar |
navbar:main |
Breadcrumb / nav bar | NavBar |
global-overlay |
Full‑screen portal target | (empty) |
toolbar:actions-before |
Portal before the main toolbar | (empty) |
toolbar:actions-after |
Portal after the main toolbar | (empty) |
navbar:before |
Portal before the main navbar | (empty) |
navbar:after |
Portal after the main navbar | (empty) |
(Need another slot? Open an issue or PR!)
# 1. Install your package next to c4_modelizer
npm i @my-scope/cool-toolbar --save
# 2. List active plugins (comma‑separated)
VITE_PLUGINS=@my-scope/cool-toolbar npm run dev
That’s it – the registry will pick them up and your components or overlays will show up instantly.
- Fork the repository and create a feature or fix branch (
feature/new‑icon‑set
,fix/overflow‑toolbar
, …). - Code your changes following the project conventions (ESLint, Prettier, TypeScript strict mode).
- Test locally (
npm test
,npm run cypress:open
). - Commit using conventional commits (
feat:
,fix:
, etc.). - Open a Pull Request — describe the motivation and link to any related issue.
The maintainers will review and guide you through the merge.
Use the Issues tab. Please include:
- steps to reproduce (gif or screenshot appreciated),
- expected vs. actual behaviour,
- browser / OS / Node version.
C4 Modelizer is available under the Polyform Internal Use License 1.0.0.
You may:
- Share — copy and redistribute the software for internal use within your organization.
- Adapt — remix, transform and build upon the software for internal use.
Under the following conditions:
- Internal Use Only — any use beyond your organization’s internal operations (including commercial SaaS, redistribution, or offering as a service) requires a separate commercial license.