# LikeC4 # Getting Started To start with the tutorial there are two options: - Open blank playground in a new tab - Install [vscode extension](https://marketplace.visualstudio.com/items?itemName=likec4.likec4-vscode) (or open-vsx) and create a new file with `.c4` extension and follow the steps: 1. ##### Prepare specification We start with defining kinds of the elements in our architecture. We need only two - `actor` and `system`: ```likec4 copy // tutorial.c4 specification { element actor element system } ``` 2. ##### Create model Start with top-level and define the model: ```diff lang="likec4" // tutorial.c4 specification { element actor element system } + model { + customer = actor 'Customer' + saas = system 'Our SaaS' + } ``` These are the first elements of our architecture model. Let's add details. 3. ##### Add hierarchy Assume our system has two main components - `ui` and `backend`. We add a new kind to the specification and update the model. ```diff lang="likec4" {5,11-12} copy // tutorial.c4 specification { element actor element system + element component } model { customer = actor 'Customer' saas = system 'Our SaaS' { + component ui + component backend } } ``` 4. ##### Add relationships **Any links** between elements (i.e. interactions, calls, delegations, dependencies, flows). You are free to define them as you like. In the model: ```diff lang="likec4" {14-15,18-19} copy // tutorial.c4 specification { element actor element system element component } model { customer = actor 'Customer' saas = system 'Our SaaS' { component ui component backend + // UI fetches data from the Backend + ui -> backend } + // Customer uses the UI + customer -> ui 'opens in browser' } ``` 4. ##### Create first diagram Diagrams are rendered from views, and views are projections of the model defined by predicates (what to include/exclude). Start with bird's eye view (_"Landscape"_): ```diff lang="likec4" {23-25} copy // tutorial.c4 specification { element actor element system element component } model { customer = actor 'Customer' saas = system 'Our SaaS' { component ui component backend // UI fetches data from the Backend ui -> backend // Customer uses the UI customer -> ui 'opens in browser' } } views { + view index { + include * + } } ``` We got this: ![landscape view](../../assets/getting-started/01.png) 4. ##### Add more views ```diff lang="likec4" {27-29} // tutorial.c4 specification { element actor element system element component } model { customer = actor 'Customer' saas = system 'Our SaaS' { component ui component backend // UI requests data from the Backend ui -> backend // Customer uses the UI customer -> ui 'opens in browser' } } views { view index { include * } + view of saas { + include * + } } ``` Imagine, we zoom in to `saas` element, and see nested elements and their relationships: ![saas view](../../assets/getting-started/02.png) 4. ##### Enrich model Let's add descriptions, define the shape of the `ui` and add a label to the relationship `ui -> backend` ```diff lang="likec4" {10,15-19,22-25,29,47-49} ins="'fetches via HTTPS'" copy // tutorial.c4 specification { element actor element system element component } model { customer = actor 'Customer' { + description 'The regular customer of the system' } saas = system 'Our SaaS' { component ui 'Frontend' { + description 'Nextjs application, hosted on Vercel' + style { + icon tech:nextjs + shape browser + } } component backend 'Backend Services' { + description ' + Implements business logic + and exposes as REST API + ' } // UI fetches data from the Backend ui -> backend 'fetches via HTTPS' } // Customer uses the UI customer -> ui 'opens in browser' } views { view index { title 'Landscape view' include * } view of saas { include * + style customer { + color muted + } } } ``` The `saas` view after changes: ![saas view after changes](../../assets/getting-started/03.png) 4. ##### Add changes Let's change the description of the `customer` and the label of `customer -> ui` ```diff lang="likec4" // tutorial.c4 specification { element actor element system element component } model { customer = actor 'Customer' { - description 'The regular customer of the system' + description 'Our dear customer' } saas = system 'Our SaaS' { component ui 'Frontend' { description 'Nextjs application, hosted on Vercel' style { icon tech:nextjs shape browser } } component backend 'Backend Services' { description ' Implements business logic and exposes as REST API ' } // UI requests data from the Backend ui -> backend 'fetches via HTTPS' } // Customer uses the UI customer -> ui 'opens in browser' + customer -> saas 'enjoys our product' } views { view index { title 'Landscape view' include * } view of saas { include * style customer { color muted } } } ``` View `index`: ![landscape view after changes](../../assets/getting-started/04.png) View `saas`: ![saas view after changes](../../assets/getting-started/05.png) :::tip[Did you see?] We changed elements in the model, and all views are updated accordingly. ::: 4. ##### Try it yourself Play with [this tutorial in playground](https://playground.likec4.dev/w/tutorial/) and try to add the following: - change [shape](/dsl/styling/#single-element) of the `customer` element - add a database (with `storage` shape) and tables like `customers` and `orders` (what relationships should be added?) - add an external system, like Stripe, and show how the backend might interact with it # Extending model You extend the model by creating new files and folders. When LikeC4 source files are parsed, they are "_merged_" into a single architecture model. You are free to organize the workspace as you want. ## Example Assume we have the following workspace: - cloud - service1.c4 - service2.c4 - ... - externals - amazon.c4 - landscape.c4 - specs.c4 This file defines the specification: ```likec4 specification { element actor { style { shape person } } element system element service } ``` This file defines the top-level elements and landscape view: ```likec4 model { customer = actor 'Customer' cloud = system 'Cloud System' } views { view index of cloud { title "Cloud System - Landscape" include * } } ``` We keep definitions of external systems separately, inside the `externals/` folder: ```likec4 model { amazon = system 'Amazon Web Services' { rds = service 'Database' } } ``` ## Extend element `extend` is a way to enrich the model and define nested elements in a separate file. We don't want to mess up the _landscape.c4_ file with the internals of the `cloud`. In a separate file we extend `cloud` and define `cloud.service1`: ```likec4 // cloud/service1.c4 model { // cloud is defined in landscape.c4 extend cloud { // extend and define cloud.service1 service1 = service 'Service 1' } } ``` The element extension inherits the scope of the target (or better say _**parent**_). For example: ```likec4 // cloud/service2.c4 model { // cloud is defined in landscape.c4 extend cloud { // extend and define cloud.service2 service2 = service 'Some Service 2' service2 -> service1 // ✅ service1 is known inside 'cloud' } } ``` ### Additional properties You can extend element with additional tags, links and metadata: ```likec4 model { extend cloud { // Add tags #additional-tag, #another-tag // Add metadata metadata { prop1 'value1' } // Add links link ../src/index.ts#L1-L10 } } ``` #### Metadata merging When extending elements with metadata, duplicate keys from both the original element and the extension are merged into arrays: ```likec4 model { component api { metadata { version '1.0.0' tags 'backend' regions 'us-east-1' } } } // In another file model { extend api { metadata { tags 'microservice' // Merges with existing 'backend' regions ['eu-west-1'] // Merges with existing 'us-east-1' owner 'platform-team' // New key } } } // Result: { version: '1.0.0', tags: ['backend', 'microservice'], // Merged and kept as array regions: ['us-east-1', 'eu-west-1'], // Merged and kept as array owner: 'platform-team' } ``` **Merging behavior:** - Duplicate values are automatically de-duplicated - If after de-duplication there's only one unique value, it's stored as a string (not an array) - Arrays from both sides are merged and de-duplicated ```likec4 model { component api { metadata { environment 'production' tags ['backend', 'api'] } } } model { extend api { metadata { environment 'production' // Duplicate value tags ['api', 'critical'] // 'api' is duplicate } } } // Result: { environment: 'production', // Single value (de-duplicated) tags: ['backend', 'api', 'critical'] // Merged and de-duplicated } ``` You can extend the same element multiple times across different files, and all metadata will be properly merged: ```likec4 // file1.c4 model { component api { metadata { version '2.0.0' tags 'backend' } } } // file2.c4 model { extend api { metadata { tags 'rest' owner 'team-a' } } } // file3.c4 model { extend api { metadata { tags 'microservice' regions ['us', 'eu'] } } } // Result: { version: '2.0.0', tags: ['backend', 'rest', 'microservice'], owner: 'team-a', regions: ['us', 'eu'] } ``` # Introduction LikeC4 is a DSL for describing software architecture. Source files must have `.likec4` or `.c4` extensions. All sources merged into _a single model_ (explained later in [extending model](/dsl/extend)). A project may look like this: - backend - service1 - model.c4 - views.c4 - service2 - model.c4 - ... - externals - amazon.c4 - ... - landscape.c4 - specs.c4 ## Top-level statements Source file should have at least one of these statements: - `specification` - defines element kinds to be used in the model, like **system**, **app**, **microservice**... - `model` - architecture elements, hierarchies, compositions and relationships - `views` - visualizations - `global` - globally shared predicates (explained later in [Views](/dsl/views/predicates/#shared-global-styles)) ```likec4 // example.c4 specification { //... } global { //... } model { //... } views { //... } ``` You have multiple statements of the same type: ```likec4 // Views group 1 views { } // Views group 2 views { } ``` :::tip For example, `views` block allows _"local styles"_, that apply to the views in the same block. This way you can group views that have same styles, and avoid boilerplate. Explained later in [Views](/dsl/views/predicates/#shared-local-styles). ::: # Model The `model` describes architecture as a set of hierarchical elements and any relationships among them. ## Element Element is a basic building block. It represents a logical part of the architecture. Any element must have a [`kind`](/dsl/specification#element-kind) and a `name` (_identifier_): ```likec4 specification { element actor element service } model { // element of kind 'actor' with the name 'customer' actor customer // element of kind 'service' named as 'cloud' service cloud // also possible with '=' and the name goes first cloud = service } ``` Element name is required for references. It can contain letters, digits, hyphens and underscore, but can't start with a digit or contain `.` | name | valid | | :--------- | :-- | | api | ✅ | | Api2 | ✅ | | \_api | ✅ | | \__Api-1 | ✅ | | 1api | ⛔️ | | a.pi | ⛔️ | ## Element Properties ### Title ```likec4 'SaaS' copy specification { element softwareSystem } model { // Title can be inlined saas = softwareSystem 'SaaS' // or nested saas = softwareSystem { title 'SaaS' // You can use `:` (optional) title: 'SaaS' } // If title is not specified, name will be used by default saas = softwareSystem } ``` :::note You can use single or double quotes: ```likec4 model { service cloud 'Cloud Service' // Or service cloud "Cloud Service" } ``` If you need quotes inside, escape with backslash: ```likec4 model { service cloud 'Cloud\'s Service' service cloud "Cloud\"s Service" } ``` ::: ### Description ```likec4 'Provides services to customers' copy model { // Can be inlined saas = softwareSystem 'SaaS' 'Provides services to customers' // or nested saas = softwareSystem { title 'SaaS' description 'Provides services to customers' } } ``` Element may have a short `summary` (optional, falls back to `description`): ```likec4 copy model { saas = softwareSystem { title 'SaaS' summary 'Provides services to customers' description ' Detailed description ... ' } } ``` If `summary` is provided, it will be shown on the diagram, and `description` in the details dialog. If you don't provide `description`, summary will be used. For inlined definition: ```likec4 copy model { // [title] [summary] saas = softwareSystem 'SaaS' 'Provides services to customers' { description ' Detailed description ... ' } } ``` ### Technology ```likec4 copy model { api = service { technology 'REST' } // Structurizr DSL style: // = softwareSystem [title] [summary] [technology] saas = softwareSystem 'SaaS' 'Provides services to customers' 'SaaS' } ``` :::tip You can define element properties in [specification](/dsl/specification#element-kind), if it is common for all of the kind: ```likec4 specification { element mobileApp { title 'Mobile App' description 'Universal mobile application' technology 'React Native' } } ``` This allows to define properties once and reuse them across the model. Properties from the element definition override the ones from the specification. ::: ### Tags Element [tags](/dsl/specification/#tag) are defined in a nested block and must come first, before any properties: ```likec4 copy model { appV1 = application 'App v1' { #deprecated description 'Old version of the application' } // multiple tags appV2 = application { #next, #serverless #team2 title 'App v2' } appV3 = application { title 'App v3' #team3 // ⛔️ Error: tags must be defined first } } ``` :::tip You can add tags in the [specification](/dsl/specification/#tag), if it is common for all of the kind: ```likec4 specification { element lambda { #serverless } } ``` ::: ### Links Element may have multiple links: ```likec4 copy model { bastion = application 'Bastion' { // External link link https://any-external-link.com // With label link https://github.com/likec4/likec4 'Repository' // or any URI link ssh://bastion.internal 'SSH' // or relative link to navigate to sources link ../src/index.ts#L1-L10 } } ``` ### Metadata Element metadata is a set of key-value pairs, defined in a nested block: ```likec4 copy model { app = application 'App' { metadata { prop1 'value1' prop2 ' apiVersion: apps/v1 kind: StatefulSet metadata: name: app-statefulset spec: {} ' prop3 '{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" } } }' } } } ``` Only string values are allowed, but you can use JSON or YAML format for complex data. ### Array Values You can also use arrays for metadata values using array literal syntax: ```likec4 copy model { app = application 'App' { metadata { tags ['frontend', 'react', 'typescript'] environments ['dev', 'staging', 'prod'] version '2.1.0' } } } ``` Mixed single and array values are supported in the same metadata block: ```likec4 copy model { api = service 'API Gateway' { metadata { version '3.2.1' maintainer 'Platform Team' tags ['backend', 'gateway', 'microservice'] regions ['us-east-1', 'eu-west-1'] critical true } } } ``` Here are more examples showing various mixed metadata patterns: ```likec4 copy model { // E-commerce application with mixed metadata types frontend = application 'Frontend App' { metadata { framework 'React' version '18.2.0' features ['shopping-cart', 'user-auth', 'payment', 'search'] deployment_targets ['staging', 'production'] team_lead 'Alice Johnson' developers ['Bob Smith', 'Carol Davis', 'David Wilson'] release_cycle 'weekly' supported_browsers ['Chrome', 'Firefox', 'Safari', 'Edge'] accessibility_level 'WCAG 2.1 AA' has_mobile_app true } } // Database service with operational metadata database = service 'PostgreSQL Cluster' { metadata { engine 'PostgreSQL' version '15.3' instances ['primary', 'replica-1', 'replica-2'] backup_schedule 'daily' backup_retention_days '30' monitoring_endpoints ['metrics', 'logs', 'traces'] alert_channels ['slack', 'email', 'pagerduty'] maintenance_window 'Sunday 2-4 AM UTC' data_classification 'sensitive' encryption_at_rest true } } // Microservice with complex deployment metadata payment = service 'Payment Service' { metadata { language 'Go' version '2.1.4' port '8080' health_check_path '/health' dependencies ['database', 'redis', 'external-payment-api'] environments ['dev', 'test', 'stage', 'prod'] scaling_policy 'auto' min_replicas '2' max_replicas '10' circuit_breaker_enabled true rate_limits ['1000/minute', '100/second'] compliance_standards ['PCI-DSS', 'SOC2'] } } } ``` ### Metadata Properties Behavior **Alphabetic Ordering**: Metadata properties are automatically sorted alphabetically when displayed in element details, regardless of the order they are defined in the DSL. This ensures consistent presentation across all elements. **Property Duplications**: When the same property name is defined multiple times within a metadata block, all values are collected into an array, preserving their order of definition: ```likec4 copy model { service = component 'Payment Service' { metadata { version '1.0.0' // First value version '2.0.0' // Second value // Result: version: ['1.0.0', '2.0.0'] owner ['team-a', 'team-b'] // First: array values owner 'team-c' // Second: single value // Result: owner: ['team-a', 'team-b', 'team-c'] tags 'primary' // First: single value tags ['backend', 'critical'] // Second: array values // Result: tags: ['primary', 'backend', 'critical'] ports ['8080', '9090'] // First: array values ports ['3000', '4000'] // Second: array values // Result: ports: ['8080', '9090', '3000', '4000'] } } } ``` This behavior applies to **all duplicate keys**. All metadata properties are displayed alphabetically, regardless of definition order. ```likec4 copy model { api = service 'API Gateway' { metadata { version '3.2.1' maintainer 'Platform Team' tags ['backend', 'gateway', 'microservice'] regions ['us-east-1', 'eu-west-1'] critical true } } } ``` Here are more examples showing various mixed metadata patterns: ```likec4 copy model { // E-commerce application with mixed metadata types frontend = application 'Frontend App' { metadata { framework 'React' version '18.2.0' features ['shopping-cart', 'user-auth', 'payment', 'search'] deployment_targets ['staging', 'production'] team_lead 'Alice Johnson' developers ['Bob Smith', 'Carol Davis', 'David Wilson'] release_cycle 'weekly' supported_browsers ['Chrome', 'Firefox', 'Safari', 'Edge'] accessibility_level 'WCAG 2.1 AA' has_mobile_app true } } // Database service with operational metadata database = service 'PostgreSQL Cluster' { metadata { engine 'PostgreSQL' version '15.3' instances ['primary', 'replica-1', 'replica-2'] backup_schedule 'daily' backup_retention_days '30' monitoring_endpoints ['metrics', 'logs', 'traces'] alert_channels ['slack', 'email', 'pagerduty'] maintenance_window 'Sunday 2-4 AM UTC' data_classification 'sensitive' encryption_at_rest true } } // Microservice with complex deployment metadata payment = service 'Payment Service' { metadata { language 'Go' version '2.1.4' port '8080' health_check_path '/health' dependencies ['database', 'redis', 'external-payment-api'] environments ['dev', 'test', 'stage', 'prod'] scaling_policy 'auto' min_replicas '2' max_replicas '10' circuit_breaker_enabled true rate_limits ['1000/minute', '100/second'] compliance_standards ['PCI-DSS', 'SOC2'] } } } ``` ```likec4 copy model { api = service 'API Gateway' { metadata { version '3.2.1' maintainer 'Platform Team' tags ['backend', 'gateway', 'microservice'] regions ['us-east-1', 'eu-west-1'] critical true } } } ``` ```likec4 copy model { api = service 'API Gateway' { metadata { version '3.2.1' maintainer 'Platform Team' tags ['backend', 'gateway', 'microservice'] regions ['us-east-1', 'eu-west-1'] critical true } } } ``` ## Using Markdown You can use markdown in `description` (and `summary`) with triple quotes: ```likec4 copy model { mobile = application { title 'Mobile Application' description ''' ### Multi-platform application [React Native](https://reactnative.dev) ''' } web = application { description """ ### Web Application > Provides services to customers through > the web interface. | checks | | | :--------- | :-- | | check 1 | ✅ | | check 2 | ⛔️ | | check 3 | ✅ | """ } } ``` ## Structuring Model Any element is a container and can contain other elements. This way you define the structure and internals of the element. ```likec4 filename="nested-elements.c4" model { // service1 has backend and frontend service service1 { component backend { // backend has api component api } component frontend } // or use '=' service2 = service { backend = component { api = component } frontend = component } } ``` Nested elements are _"namespaced"_, the parent name is used as a prefix. So, the model above has the elements with these fully qualified names: - `service1` - `service1.backend` - `service1.backend.api` - `service1.frontend` and: - `service2` - `service2.backend` - `service2.backend.api` - `service2.frontend` :::caution It is not possible to have elements with the same name on the same hierarchy level. It is explained in detail in [references](/dsl/references). ```likec4 filename="nested-elements.c4" model { service service1 'Service 1' { component backend component backend // ⛔️ Error: 'service1.backend' already defined } service service2 'Service 2' { component backend // ✅ This is OK - 'service2.backend' component legacy { component backend // ✅ This is OK - 'service2.legacy.backend' } } component backend // ✅ This is OK - 'backend' } ``` ::: # View Notations View notations (or key/legend) provide information about the meanings of shapes and styles. It's important to provide a key explaining the difference in colors and shapes. It is possible to define global notations (per element kind) and local (per view). #### Global notations As comes from the name, global notations apply to all views, and defined in the `specification` block: ```likec4 copy {4, 12} specification { element customer { notation "Person, Customer" style { shape person color green } } element staff { notation "Person, Staff" style { shape person } } } ``` Notations will be applied to all views with these elements and displayed as:
![notations](../../../assets/notations.png)
Live example. Expand the following view and click on help icon in bottom right: #### Local notations Local notations override global ones and apply to a specific view. ##### With style predicate When you change the style, you can add a notation to explain the meaning: ```likec4 view { style webApp1, webApp2 { notation "Application under development" color amber } style element.tag = #deprecated { notation "Deprecated" color muted } } ``` Notations are not merged or grouped, the last one will be applied. :::tip You may have same notation for different styles: ```likec4 view { style webApp1 { notation "Web Application" color amber } style webApp2 { notation "Web Application" color green } } ``` ::: ##### With overrides Define notation when include elements: ```likec4 view { include * where kind is microservice and tag is #deprecated with { notation "Deprecated microservice" shape rectangle color muted } } ``` This override has the highest priority, then defined within style predicates, and finally global notations. :::note Feel free to share your ideas in GitHub discussions how to improve notations, make reusable or more flexible. ::: # References LikeC4 uses the lexical scope with hoisting, almost like in JavaScript. ## Scope To understand references, we need to understand scopes first. Example: ```likec4 model { service service1 { component api component frontend } } ``` Every element is unique in the model, so we can add a relationship referencing them, like: ```likec4 'frontend -> api' model { service service1 { component api component frontend } frontend -> api } ``` But if we add `service2` with another `api`: ```diff lang="likec4" model { service service1 { component api component frontend } + service service2 { + component api + } frontend -> api // ⛔️ Error: 'api' not found } ``` The reference is ambiguous, as there are two `api` components in the model. Every element creates a new scope inside `{...}`, so `api` is unique inside `service1` and `service2`, but not in the scope of `model`. We can resolve by moving the relationship to the scope of `service2`: ```likec4 {9-11} model { service service1 { component api component frontend } service service2 { component api frontend -> api // ✅ This is OK, // 'api' is unique in 'service2' // 'frontend' is unique in 'model' } } ``` ## Hoisting In LikeC4, the element, besides being hoisted in its scope, also _"bubbles"_ to the upper scopes, if it stays unique. We may reference something that is not yet declared but will be hoisted later. The relationship on line 8 references `graphql` defined below on line 15: ```likec4 showLineNumbers copy model { service service1 { component api component frontend frontend -> api // ✅ This is OK, references to 'api' from 'service1' frontend -> graphql // ✅ This is OK, references to unique 'graphql' } frontend -> api // ⛔️ Error: 'api' is ambiguous service service2 { component api component graphql frontend -> api // ✅ This is OK, references to 'api' from 'service2' } } ``` ## Fully qualified names Top-level elements (placed directly in the `model` block) are available globally. You can use fully qualified names (FQN) to reference nested elements. Example: ```likec4 {11,12} model { service service1 { component api component frontend } service service2 { component api } frontend -> api // ⛔️ Error: 'api' not found frontend -> service1.api // ✅ This is OK frontend -> service2.api // ✅ This is OK } ``` Or even: ```likec4 {6} model { service service1 { component api component frontend { -> api -> service2.api // references to outer scope } } service service2 { component api } } ``` Some parts may be omitted, if FQN stays unique: ```likec4 model { service service { component backend1 { component api } component backend2 { component api component graphql } } frontend -> service.backend1.api // ✅ Non-ambiguous fully qualified name frontend -> backend1.api // ✅ This is OK, 'api' is unique in 'backend1', // and 'backend1' is unique in the model // We may omit 'service' frontend -> backend2.api // ✅ This is also OK frontend -> service.api // ⛔️ Error: 'api' is ambiguous in 'service' frontend -> service.graphql // ✅ This is also OK, we omit 'backend2' // as 'graphql' is unique in 'service' } ``` # Relationships Relationships describe the connections, data flows and interactions within your model. ## Relationship definition Relationships are defined with the **`->`** operator: ```likec4 model { customer = actor 'Customer' cloud = service 'Cloud' customer -> cloud } ``` Relationships may be nested ```likec4 model { service cloud { component backend component frontend frontend -> backend customer -> frontend } } ``` In nested relationships you can use `it` or `this` to refer parent: ```likec4 model { customer = actor { // as a source it -> frontend // as a target frontend -> this } } ``` Nested relationships may be _"sourceless"_, then the source is the parent element ```likec4 model { actor customer { // same as customer -> frontend -> frontend } service cloud { component backend component frontend { // same as frontend -> backend -> backend } } } ``` :::caution _"sourceless"_ relationships must be nested: ```likec4 model { -> backend // ⛔️ Error: model can't be a source } ``` ::: ## Relationship kinds Relationships can be "kinded": ```likec4 specification { element system // Define relationship kind relationship async relationship uses } model { system1 = system 'System 1' system2 = system 'System 2' system1 -[async]-> system2 // Or prefix with '.' to use the kind system1 .uses system2 } ``` This makes it possible to add richer semantics to the interactions between elements, for example, from a technology perspective (REST, gRPC, GraphQL, Sync/Async, etc.) or from a business perspective (delegation, informing, accountability, etc.). You can define whichever relationship types best fit your context. ## Relationship Properties ### Title Relationships may have a title (and better to have one): ```likec4 'opens in browser' model { customer -> frontend 'opens in browser' // or nested customer -> frontend { title 'opens in browser' } } ``` ### Description ```likec4 'Customer opens...' model { customer -> frontend 'opens in browser' { description 'Customer opens...' } // Or in a shorter way customer -> frontend 'opens in browser' 'Customer opens...' } ``` Same as for elements, you can [use markdown](/dsl/model#using-markdown) in `description` with triple quotes: ```likec4 model { customer -> frontend 'opens in browser' { description ''' **Customer** opens the frontend in the browser to interact with the system | checks | | |:--------- |:-- | | check 1 | ✅ | | check 2 | ⛔️ | | check 3 | ✅ | ''' } } ``` ### Technology ```likec4 model { customer -> frontend 'opens in browser' { technology 'HTTPS' } // Or in a shorter way // order is [title] [description] [technology] customer -> frontend 'opens in browser' 'Customer opens...' 'HTTPS' } ``` ### Tags Relationships may have tags: ```likec4 model { // inlined frontend -> backend 'requests data' #graphql #team1 // or nested customer -> frontend 'opens in browser' { #graphql #team1 } } ``` ### Links Relationships may have multiple links: ```likec4 copy model { customer -> frontend 'opens in browser' { // External link link https://any-external-link.com // With label link https://github.com/likec4/likec4 'Repository' // or any URI link ssh://bastion.internal 'SSH' // or relative link to navigate to sources link ../src/index.ts#L1-L10 } } ``` ### Navigate To Relationship may have a `navigateTo` property, which is a link to [dynamic view](/dsl/views/dynamic). This allows to _"zoom-in"_ and see more details about this relationship. ```likec4 copy model { webApp -> backend.api { title 'requests data for the dashboard' navigateTo dashboard-request-flow } } ``` ## Relationships Metadata Same as [elements metadata](/dsl/model/#metadata): ```likec4 model { customer -> frontend 'opens in browser' { metadata { prop1 'value1' prop2 '{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" } } }' } } } ``` # Specification In the `specification` you define your notation. ## Element kind Defines the element kind, that are used in the model: ```likec4 copy specification { // Define whatever you want element user element cloud element system element application element component element controller element microservice element queue element restapi element graphqlMutation element repository element database element pgTable } ``` Later you will learn that element kinds may have properties and define style: ```likec4 specification { element queue { title 'Kafka' description 'Kafka queue' technology 'kafka topic' notation 'Kafka Topic' style { shape queue } } } ``` ## Relationship ```likec4 specification { relationship async relationship subscribes relationship is-downstream-of } ``` More in the [relationships](/dsl/relationships/#relationship-kinds) ## Tag Tags may be used to mark, group, filter elements/relationships/views, or give some additional semantics, like `#deprecated`, `#epic-123` or `#team2`. ```likec4 specification { tag deprecated tag epic-123 tag team2 } ``` You can assign colors: ```likec4 specification { tag deprecated { color #FF0000 // or `rgb(255 0 0)` see below } } ``` You can add tags to element kinds: ```likec4 specification { // Now every kafka-topic will be marked with the infra and data-lake tags element kafka-topic { #infra #data-lake } tag infra tag data-lake } ``` ## Color Custom colors could be defined to extend built in themes. Being defined in specification they could be used later along with the theme colors. ```likec4 specification { color custom-color1 #F00 color custom-color2 #AABBCC color custom-color3 rgb(255, 0, 0) color custom-color4 rgb(100 150 200) color custom-color5 rgba(44, 8, 128, 0.9) color custom-color6 rgba(255, 200, 100, 50%) element person { style { color custom-color1 } } } ``` :::note Only a base color with a 3, 6 or 8-character hex code, and `rgb()`/`rgba()` is supported. The reason is that to draw a node, we need not only a fill color but also a border color and text color. The same applies to the color of edges, where a line color, label background color, and text color are required. Therefore, the [color palette generator from the Mantine library](https://mantine.dev/colors-generator/) is used to build a color scheme from the provided base color. ::: # Styling LikeC4 provides advanced customization capabilities. You can change colors, shapes, sizes, and icons of elements and relationships. ## Element There are multiple ways to style elements: - Style all elements of a kind in `specification` - Specific for an element in `model` or `deployment` - Override styles in `view` (more on this in [next section](/dsl/views/predicates/#style-predicates)) ### Elements of a kind To style all elements of a kind, use the `style` block in `specification`: ```likec4 copy specification { element user { style { // every element of 'user' kind shape person // has 'person' shape color amber // and amber color } } element frontend { style { // every 'frontend' displayed as browser shape: browser // ':' is optional, but if you prefer } } } ``` ### Single element To style a specific element, use the nested `style` block. Element styles override ones from the kind: ```likec4 copy specification { element actor { style { shape person color red } } } model { customer = actor 'Customer' { style { // inherits shape and overrides color color green } } } ``` ### Per view [Next section](/dsl/views/predicates/#style-predicates) clarifies how to customize elements per view. ## Style properties ### Shape ```likec4 "shape person" copy specification { element actor { style { shape person } } } ``` Available shapes: `rectangle` (default), `storage`, `cylinder`, `browser`, `mobile`, `person`, `queue`. ### Color ```likec4 "color red" copy specification { element actor { style { color red } } } ``` Available colors: `primary` (default), `secondary`, `muted`, `amber`, `gray`, `green`, `indigo`, `red`. :::tip It's also possible to use custom colors defined in [specification](/dsl/specification/#color). ::: ### Size Size of an element is controlled by following properties: | property | explanation | | :-------- | :----- | | size | size of the shape | | padding | space around element's title | | textSize | font size of element's title | Every property accepts: `xsmall`, `small`, `medium`, `large`, `xlarge` (or short `xs`, `sm`, `md`, `lg`, `xl`). Default size is `medium`. When shape size is `xsmall`, only element's title is displayed. ```likec4 "size large" "textSize xl" specification { element element { style { size large textSize xl } } } ``` ### Opacity If element displayed as a group (like a container), you can set opacity: ```likec4 "opacity 10%" specification { element element { style { opacity 10% } } } ``` ### Border If element displayed as a group (like a container), you can change border style: ```likec4 "border dotted" specification { element element { style { opacity 10% border dotted } } } ``` Supported values: `dashed` (default), `dotted`, `solid`, `none` ### Multiple To display element as multiple instances, set `multiple` to `true`: ```likec4 specification { element element { style { multiple true } } } ``` ### Icon Elements may have an icon - any browser-supported image (png, svg, webp, etc.): ```likec4 copy model { pg = service 'PostgreSQL' { style { // Publicly available with `https://` icon https://icons.terrastruct.com/dev%2Fpostgresql.svg // or local image, relative to current file icon ../postgresql.svg } } } ``` :::tip `icon` can be defined as a property and skip `style` block ```likec4 copy model { pg = service 'PostgreSQL' { icon https://icons.terrastruct.com/dev%2Fpostgresql.svg } } ``` ::: :::tip Use `none` to unset `icon` ```likec4 copy pg = service 'PostgreSQL' { icon none } ``` ::: ### Aliased icons With `@` prefix, you can use aliased folders (learn more in [configuration](/dsl/config/#image-aliases)): ```likec4 copy model { pg = service 'PostgreSQL' { style { // local images, based on aliased folders icon @/postgresql.svg } } } ``` ### Bundled icons LikeC4 includes a set of icons from these packs: - `aws:` from aws-icons.com - `azure:` from microsoft.com - `gcp:` from gcpicons.com - `tech:` from techicons.dev Example: ```likec4 copy model { fn = service 'Lambda Function' { icon aws:lambda } k8s = service 'K8s Service' { icon gcp:google-kubernetes-engine } pg = storage 'PostgreSQL' { icon tech:postgresql } } ```

:::tip Use VSCode code completion to explore available icons. ::: ## Relationship There are multiple ways to style relationships: - Style all relationships of a kind in `specification` - Specific relationship in `model` - Customize per `view` ([explained here](/dsl/views/predicates/#relationship-customization)) ### Relationships of a kind Relationships can be styled in [specification](/dsl/relationships/#relationship-kinds): ```likec4 specification { relationship async { color amber line dotted head diamond tail vee } } ``` ### Specific Relationship ```likec4 model { customer -> ui 'opens in browser' { style { line solid color amber } } } ``` ### Relationship per view [Next section](/dsl/views/predicates/#relationship-customization) clarifies how to customize relationships per view. ## Relationship properties Besides the `color`, relationships may have the following properties: ### Line | line | example | | :----- | :-----: | | dashed | .. | | solid | .. | | dotted | .. | By default, the line is `dashed`. ### Arrow type The arrow type can be set for the head and the tail of the relationship: | type | example | | :-------- | :-----: | | normal | .. | | onormal | .. | | diamond | .. | | odiamond | .. | | crow | .. | | vee | .. | | open | .. | | none | .. | > `onormal` means "outlined normal", i.e. no fill > `odiamond` - "outlined diamond" By default, the head is `normal` and the tail is `none`. ```likec4 model { customer -> ui 'opens in browser' { style { head diamond tail crow } } } ``` ## Styles customization How to override default styles is explained in project configuration # Generated Views ### Relationships browser ### Relationship decomposition # Deploy to GitHub Pages :::note You can check likec4/template repository. It builds and deploys static website to GitHub Pages [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/~/github.com/likec4/template) ::: ## GitHub Actions workflow Use [LikeC4 GitHub Action](/tooling/github/) to build and deploy static website to GitHub Pages. ```yaml # Sample workflow for building and deploying a website to GitHub Pages name: Deploy Pages on: # Runs on pushes targeting the default branch and c4 files push: branches: ["main"] # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write # Allow only one concurrent deployment, skipping runs queued between the run in progress and the latest queued. # However, do NOT cancel in-progress runs, as we want to allow these production deployments to be completed. concurrency: group: "pages" cancel-in-progress: false jobs: # Build job build-pages: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Pages id: pages uses: actions/configure-pages@v4 - name: Build uses: likec4/actions@v1 with: action: build output: dist # required if you don't set a custom domain for the repository # https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages#types-of-github-pages-sites base: ${{ steps.pages.outputs.base_path }} likec4-version: latest - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: ./dist # Deployment job deploy-pages: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build-pages steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ``` ## Use LikeC4 CLI You can use [LikeC4 CLI](/tooling/cli), example of `build-pages` job: ```yaml jobs: build-pages: runs-on: ubuntu-24.04-arm steps: - name: Checkout uses: actions/checkout@v4 - name: Setup node uses: actions/setup-node@v4 - name: Setup Pages id: pages uses: actions/configure-pages@v4 - name: Build run: | npx likec4 build \ --base ${{ steps.pages.outputs.base_path }} \ --output dist - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: ./dist ``` # Embed to website This page is under construction. # Preview changes in PR # Deploy a static website import { PackageManagers } from 'starlight-package-managers'; ## Pre-requisites You must have [Node.js](https://nodejs.org) installed. Minimum supported version is 20.x, but using the latest stable version is recommended. You can also use [LikeC4 Docker](/tooling/docker/). ## Build Run `build` command, that prepares a folder with static files ready to be deployed to any host. ```sh npx likec4 build -o ./dist ``` Available options: | Option | Description | | ----------------------- | ----------------------------------------------------------------------------------------------------- | | `-o, --output` | Output directory | | `--base` | Base URL the app is being served from, e.g. "/", "/pages/" or "./" for a relocatable app | | `--use-hash-history` | Hash-based navigation, e.g. "/#/view" instead of "/view" | | `--use-dot` | Use local binaries of Graphviz ("dot") instead of bundled WASM | | `--title` | Base title of the app pages (default is "LikeC4") | | `--output-single-file` | Generates a single self-contained HTML file | To see all available options run: ```sh npx likec4 build -h ``` ## Deploy LikeC4 CLI uses Vite to build the website. All the examples from [Vite documentation](https://vitejs.dev/guide/static-deploy.html) apply to LikeC4, just replace `vite build` with `likec4 build`. # Enforce and validate your model import { PackageManagers } from 'starlight-package-managers'; Sometimes you may want to enforce custom rules on your model or validate its consistency. Here's a simple recipe for how to do that. :::note Check likec4/template repository for a complete example. [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/~/github.com/likec4/template) ::: ## Test your model In this example we'll use [Vitest](https://vitest.dev) together with the [LikeC4 API](/tooling/model-api/). The LikeC4 API gives you methods to query and traverse the model - perfect for writing tests that enforce your rules. ### Example Suppose we want to enforce that every element of kind `app` has a `technology` specified. ```ts // test/metadata.spec.ts import { LikeC4 } from './LikeC4' import { test } from 'vitest' // Initialize and compute LikeC4 Model const likec4 = await LikeC4.fromWorkspace('..') const model = likec4.computedModel() // With `test.for` we generate tests for each element of kind `app` // This improves the output, showing each test failure separately test.for( model // Select elements of kind `app` .elementsWhere({ kind: 'app' }) // Map to array of [id, element] tuples, we need it for test names .map(e => [e.id, e] as const) .toArray(), )('app "%s" has technology', ([, e], { expect }) => { expect(e.technology).toBeTruthy() }) // Or we can use `expect.soft` to accumulate all errors test('elements of kind `app` have technology', ({ expect }) => { expect.hasAssertions() for (const app of model.elements()) { if (app.kind !== 'app') continue // Skip non-app elements expect.soft(app.technology, `app ${app.id} has no technology`).toBeTruthy() } }) ``` :::note Calls to `LikeC4.fromWorkspace('..')` are memoized (by absolute path). You can call it multiple times without performance impact - for example in `beforeEach`. You can also use Vitest's [`provide`](https://vitest.dev/advanced/api/vitest.html#provide) function to reuse the same workspace path. ::: ## Pre-generate model We can optimize our tests by pre-generating the model in [global setup](https://vitest.dev/config/#globalsetup): ```ts // global-setup.ts import { execSync } from 'node:child_process' export default function() { execSync('npx likec4 gen model -o ./test/likec4-model.ts', { stdio: 'inherit' }) } ``` The generated model is fully typed, giving us type checking and autocompletion in tests: ```ts // test/metadata.spec.ts import { likec4model } from './likec4-model' import { test } from 'vitest' test('Relationships should have metadata', ({ expect }) => { expect.hasAssertions() for (const r of likec4model.relationships()) { expect.soft( r.getMetadata('key'), // here we get type checking `Relationship ${r.source.id} -> ${r.target.id} has no metadata` ).toBeDefined() } }) ``` We can go further and use [test context](https://vitest.dev/api/#test-context) to improve our experience: ```ts // test/likec4test.ts import { likec4model } from './likec4-model' import { test } from 'vitest' interface LikeC4TestFixtures { likec4: typeof likec4model } // This wil be our test function with the model in the context export const likec4test = test.extend({ likec4: async ({}, use) => { await use(likec4model) }, }) ``` Now refactor tests to use it: ```ts // test/metadata.spec.ts import { likec4test } from './likec4test' likec4test('Relationships should have metadata', ({ expect, likec4 }) => { expect.hasAssertions() for (const r of likec4.relationships()) { expect.soft( r.getMetadata('key'), // here we get type checking `Relationship ${r.source.id} -> ${r.target.id} has no metadata` ).toBeDefined() } }) ``` ## Conclusion This approach makes it easy to enforce custom constraints and validate your model consistency. Running these checks in CI pipeline is fast and provides immediate feedback when the model breaks your rules. # Big Bank Example This is LikeC4 version of the Big Bank plc example by Simon Brown, based around a fictional online banking system. It gives an overview of the LikeC4 syntax and how it compares to Structurizr DSL, what is similar and what is different. ### System Landscape The landscape view is a high-level overview of the system, showing the major systems and how they relate to each other. Structurizr uses the `systemlandscape` for this view. There are no special view types in LikeC4. Each view is defined by the [predicates](/dsl/views/predicates/) what to include/exclude, like `include *` below. We create `index` view _"of bigbank"_, as this is the top-level system which landscape we want to visualize. ```likec4 views { view index of bigbank { title "Big Bank - Landscape" include * } } ``` Result: ```structurizr views { systemlandscape "SystemLandscape" { include * autoLayout } } ``` Rendered with PlantUML: ![](https://www.plantuml.com/plantuml/img/lPTVRzis4C3VzIaEpckQsK6QUe5ir2dIOS223dEsFYHzeAcJJPWYDHvT9olwtNUa9LlnfCpjiiX0uf2ykz_zfVAMl71-LUY4q0DXAPpmre6n1XEll4QOFf370duH9YCfIamIRqlm5cRMUrj0fXnAhwn94beg4pGKy2K14CxPzNjh_5JzXJ3eyrA6kLXfVuFtVgBLt1HeF4Z-WOvtrqwK7rNc5t3Q3wSBSN-DH6AEbOP-yZL95Yf3O2I7rZy4d9tL02BCN6EQJcwkn-E8SIdaSkxiocJlh6RNZdwC3-y4jcejZqyuRLs6hbezduKFhz92P7Qjp1noeGaPOsUqH4VMf6cmyo7FbSIf5td-mjRVhOYJWMwAxlCB8pIYVbK8fJS5y_B6VmvbPD1V2iFRywSn1eFpuImx1AC_EniEpmx7k1RAvBo61t7yJ-NmQ_mjTCLGLqSNHNzuTlfcTb0qNf_Yw_DMYcGR1EXTgZa4sr1gsOCq3TRAGML-X_jRBT6mqJXy9EFJBh95FLHWOsFg7qAMjYAPe0Lc2OjiOWUzqQWE5rJT1B6TxanAF8Sk1ltnxIQ-dz8VmihupjoPhGnLCXHaOAtyGXdm2uGPn-Ca1AEZLmL1syuZif0vs4oPGw0cMQ6C8iz3dGSqy0W7MfMbTVu9l7yECYRRMsIeHhP7estHGrLnrRNHn9lQtA-5gs-K9kUKxnma45g3pIEDv8M857InRkkAoeUPNV6TKGMAZa58oPNi-HkxQKZ4kuZ_GY-ZEvtzo5wrx-qxT_KgZBLly-Y6VL1n80aDrqewItmOhkxbWlKYu4OdHS78-NXsCbnZOP-8RxS3EIPQspNjuXKkxf3qp8arIAxzQ7jpMx9D_h9lgnwhtvVPDlkV5QvRSiffLoxcl97JKBA5M2AKuY6yBL3axHhrx47hLfXEkoQgX-FMexhj_gMPjbxKHHfOZsCugnPbdO_u8sptB6RSZiqawTY0azkZD_tmEUREbKl3BqYZqqydutR7uG9wgGnJyg8I6__1qQUQa76rC18RaQPjg655hAYNj6lk16blgXQIdz3lG9hra3jRF2DGev5QAAR8BxPrFrO6cXATQhiozBI9q06zRyzksytGOcb2Iu8_LqXn2B8fBkd79pOLFXpVtg36Si7l-NMDzv9M8_yzpxyJqdUupBEeHTd7QdXHd5vrmiBPvmBTk-gUJUzRhWB-J-jlZ)
:::note[Try it online] Both LikeC4 and Structurizr have online playgrounds: ::: ### System Context ```likec4 views { view context of bigbank { title "Internet Banking System - SystemContext" include bigbank, mainframe, internetBankingSystem, email, customer style * { color secondary } style bigbank, internetBankingSystem { color primary } } } ``` Result: ```structurizr views { systemcontext internetBankingSystem "SystemContext" { include * animation { internetBankingSystem customer mainframe email } autoLayout description "The system context diagram for the Internet Banking System." properties { structurizr.groups false } } } ``` Rendered with PlantUML: ![](https://www.plantuml.com/plantuml/img/hPPDRzim38Rl-XL4Ucsx9EX7LhX1Fz0DEnGObc8xj3tGDfq8aIL3KkPcnVxxACTEtS8kCg3E8GafaWyflKekd4VhLxaIZZmuAj2YDnQqqIIyz8hWc_PaDNklK2-bdiDixJpbSD1yk3QyuiaBuKq1ta1il3SvfD9IugNHGZYE2vbpU1O0QAqflt3GJFuX60tPb5A6A-NlECsbadETa_QGKAZCtj9YyKkcVW7ZOLYTuzCjEOTpgZCCnLyX5Z8bO21BngyBQbSD8AZdYgBe9aUfDk3JQOLpXSboRirAfzT6SUjxdwk7FO6njcNL4rORcvAjhITnUB7LBJ0rPHXEXieHutGMRqPPJ_9zZD7eP1Adks0CuwF9v6XtZ5kKEcCRxSJnmUCeo3gZe-XxzOwgPhXUEJuqPFieXfEZyMcyqpIEntHyqaaXjaE0mPMSLoSQ2fKC88f2jM9Kbt_1_T54j6gq5kybrTCTiaNTDM1JO-RNOPUs8Icw1QM2Grhuv-VRNNmZp0STEPR5sMWulP-Pp9TegNOzHf-2Q_gW7_ICPKuEK2am6VW5GS86YEjSdDlO78D-0MEpOZdME-Js0PE4jyRp9szHEwnapj_89uTXbhq731rq-zusTtCGTFf_7Trn3x9I62gubOarZXV3pLEouBm4jCdfgi2Ay_Lbm70jmrkYdcjmHxPBfKpfNcOOxidVaigEEVDMI5ljGslEg5E-zfO41Qx3vUWg_hxxX3lfT0K9RFGNEceKkEseqM2xfysYLwriksWEJc3TXrBo1LqqEL_nLx3zWcVS7gSGFGuulDyx7ORNlb_8PAdvz-1y_7WuxMuSpc0G9K6APtNOz2TFtpM4ZAjG9zIQjDkE6bQE4mr4T-PUaEw3ta7obVmBIFiyPDRa7mZKEjmT53FIgMiqKXTeH_IcxTkXzqi41Gp41LlupyS_) ### Containers ```likec4 views { view ibsContainers of internetBankingSystem { title "Internet Banking System - Containers" include *, -> customer } } ``` Result: ```structurizr views { container internetBankingSystem "Containers" { include * animation { customer mainframe email webApplication singlePageApplication mobileApp apiApplication database } autoLayout description "The container diagram for the Internet Banking System." } } ``` Rendered with PlantUML: ![](https://www.plantuml.com/plantuml/img/rLVVJnj747w_ls947lfYZEC28SXO8OIgYUB6xQ4a4kHXxcvihxZRFUtkOMZL_xspzuipt1aZA6hn0_B-cc--cVbc_CvOr3PBOi-G1KCfQhHAWrIIF2jiJF1HMjAITz-Zl1Ho3lwDiPJ0DfmeQL58qiRph4h1AWYKjIg1I6WAhL3IympVSQycy9S7W5ghvIzypnT_4hmUy594CynYUqRNzZWMSvcGj12IiygxIutfDn7P1Gm7xdI2rnCo1kTKlZ3m_lQyXOW869KcPMzIQbfrG53EOngDAazAHme_-iVJZyTf6eiG7U3nECVv7iFBkLQPZ4vKp7oysltv88hsUCU1BzTc-PyxhNH4kbmTqlxmhJjf5XYffMDiXh4XndcBAaAB0PfEK1_A_HU2qo3fD44H5oYwZB_D_pOnlX-up-R69vnECrwZ5JPU7aTxK2Og435nihnGL5AkB2L7esd-7-CIpKbckCP8Tw4O7EmEtmIRe1Zi3Mbl_wcq-CBjJhaaNxwE7WJtZOB_7geAnaeiWjs3WzrL2FNQVMDUaHomTNHKfKSjq5M2NCYHOP4yV3qOdnU7VenstChuGbx8OmZBSw1cO1S40T_lmLAmsARLcsuDC0oPB6lwMx0k6z50nwPNC-80rrL-fwKqyVfgPfUegTng0lUvGP41ZECAUCWBELBt5h-jT9A76p1GcQtzDBtQanvOZT9WtgJu6zamTwvJWDYtzhrLrqwtdNm-pfypza3aQS9yJqIebU73S7eTBlXT0YhUD4L2EvIh6itG6WjU4r17xzu0c3EpBdTNlEOYWqQLDP-lPuH3-8HN22WZy5Fji4o-dZHf-42nkE8WvH7biSIAa0FB47WSSBVSSkrF5MoJQm-aB3w0jmZwdV3l2mBJjyd9kpxpsF1qdwTQNV7eOkuxDijadfiO2ti3Lu9VWK_-bzzsVZqxc_h0rjVuU6-awVHnzQIRnih1mxbO3I4TldtH638NrIdmmmKbs5AfcM6DqJGNXeikhyj6ZVPWWMP14M364PQsX0AXfiXzPRtBovQ7FeZLl56h3qvEdLvNP-x6jEXaswwLRHXRFzGYBVBpMCwp67LhU1jQzK2aUOQkvIeFEzyH6fPSOG6Fpipa6j-xEd4d4QqNxh9IZ3sEa0uYlcE2dDtjdaCi4c4vlYOBt6-C9z6G550YkX8XhI6YEQbrEbyVSksqeQ3GFt_rPk0-FRiGuQNar3iQ_kYFkxeR7CBMA7Gj-x2yEFP9HgPKRbOqjULrtUqqjeaejLZ-Aeom1W8nTypsGvNiY81Ba9_WyAsUBQ9vC3xFHQMM_kV2sffThP1fQQhMp0aHe-Hox-LLbU0bPr6AD-vdeIdyU3Q0TGOy2wYD0lOlKNZRF5Kn4PMC_6IQqj65iTtA8w6kjf8MZ9ESDTStGzP31xKx8MzRtMrMFXJ3_mVUEsOV2RfkjlVX_a78aZFJAibpTAbPTTU3zd__l7DsCdqknEtpyHE9_Jxv_BoyDeL-lMvNTVsErx8a_WS0) ### Components ```likec4 views { view apiApp of internetBankingSystem.apiApplication { title "API Application - Components" include * style * { color muted } style singlePageApplication, mobileApp { color secondary } style apiApplication, apiApplication.* { color primary } } } ``` Result: ```structurizr views { component apiApplication "Components" { include * animation { singlePageApplication mobileApp database email mainframe signinController securityComponent accountsSummaryController mainframeBankingSystemFacade resetPasswordController emailComponent } autoLayout description "The component diagram for the API Application." } } ``` Rendered with PlantUML: ![](https://www.plantuml.com/plantuml/img/rLXHKziu47xthzYXhn1IxcWf0va6ZixH8TSSfdUTWJviR2NHO4iPIIPmD_VVRsKxnZQsun0ADBmGMT9-twVLxkTyr0QLYSB0qSo0PZDKQAG28GLp33S1WrDXc1Bqz0Z5DHSJSE-qOI5iGNzu2ltPBE0U6Yu5ZHpBS4OhXT6EO-GCZ8IHD4Q6u7F5F3lBSJJjOkE4yAy3W4h9-ISfZCl_OV2sIqC-6sCKc0jsQ_e1duYGDWIFMHpqTAvmzZVtpHHskdPsYBS3fZLEMBf3r_dFSQRSPq0u5PFcRiRoKIqG59E07HmikANK4cOTufMZrUbxdeo8aXk58QgxOyAgP10mrUl538xGkvueck8Vou2qUxEtEngDBJHBAnthnXyxMogVgNHqrz_RmJqQrLFqvTogEyP0Cs9mZzT7WoFKjN1_Iv_NmVdrbptVtot26SUVCfmTzcxdm-uIEAlATn8Y3xB4U2RDSY3Z-0cqkk0VuayRXTwDx5_xu0FaOanZh23Hy-DeTLBLO3-XXpvx_dEhXZoG8numGlrAi_sSKI4TejPp2lNifM9Lj2xp8iNDtGjUptOugGkSYjSdfyljqo7rdXpQrvcP2rILSNA86c8xMKZOE3pCgaN965o90qsDUFzjjtVfoh6PesBfe-y7s_PHxqfS2PTi1jE0GG1o36RAmAC16ALxqTvIXOclm96Co014cem4KteJCEt8cs0K2esniQ1lp7YT3MYekgYX-bcHsicMxHchyRaW1Zm-1mnWm3qbDKs6arjlIlioOCcUEZaRYxBOc0XQRi1PD7qBPz7WyqZiNI0cJEt35uKUuLxC0jURiX0hj8uqKvGs4wv9jbZPdCgRC4KzPJvWH8SXpCBKUOhvzYlbG2myMIq8v2Idzb9NGjIQvnJLJoxLbhrLUNjPm_uptg3hAJupWCA7lfX40Qey-w6ID-G62xcMcTf5leqZ4MSH1bGEhM_DqWrkEDerNC6SZM14TfLqR61ULnHgPQXSKBfiSTkpSjJ8y0t9ct9HpHi27d93PwkZaVNvQuaG9b1yTiCzrY13lbFNKiyc5OfBwSMYuUJZCtTdog8V_7Mye5-Fgh8ebj9l8WWjt7UROloqK-xdmW9rVPDRa3-5VX1GmiHtJrj9jTsU2_jluGoAHRJJba5LQwnLUydIiXAB5nz8ttoUIf8iCU_JQyxfVGufFvD0IO9IijdCpCfzUr6QNjdQgbExHrccsFB1mlFzgBnHSPH4bLaQwrwtk8BOoV3r_AozJlLkjQrADJiyk5IfqRp_XQ1Pd2E6rRMiMA8K2z3MjVHU9SPX4-AMQxzaGYthLYrySUjRLLvRrgBEE47ofhMQC7roPtOLaFH9HGcpxHTbNQzOKfQy1xOLeccRiYQfutfaThXCq5r875PyMU9RqvxmWvyesb8iBhGMUOKE3vtBD--xzc_JJBbtBSXQ7Uvyx_GULxrX7pOEF6lYzzDjUmEyfYjZkwb7tiSgGxeMfoGox_KkFxjV_jZ-_U9Yw6PDz1d9r1RRdvfLJN5yDawPk_fPK-sf2BnyUZqLavTEgMMPiMQYbNjd1Q-lckcMwEgFVJsOJPrjFSHrn_kgPFqn88kzT3s43mclY2txCUKXZ7E6P3R6IeRno-USuh3cs-N-URPzSJnSyMer9bajdUfLQvQ7RuEpHrM7qf7MWixzy5M1DE_koDbHj0td8ur5OV0_)

:::tip[More examples] LikeC4 has no view types and therefore any limits. Check the following examples :::
We may visualize how customers interact with the system. ```likec4 views { view customer of customer { include *, customer -> internetBankingSystem.*, customer -> bigbank.* exclude webApplication style bigbank { color muted } style customer { color green } } } ``` > Structurizr does not have customer view. Result: We may visualize interactions with the mobile application: ```likec4 views { view mobileApp of mobileApp { include *, internetBankingSystem, internetBankingSystem.apiApplication, mobileApp -> internetBankingSystem.apiApplication.* style * { color secondary } style apiApplication, internetBankingSystem { color muted } style mobileApp { color green } } } ``` Result: We may visualize interactions with the web application: ```likec4 views { view spa of singlePageApplication { include *, apiApplication, internetBankingSystem, -> singlePageApplication -> style * { color secondary } style internetBankingSystem { color muted } style singlePageApplication { color green } } } ``` Result: # Visual Ops with LikeC4 When you're dealing with thousands of scheduled jobs and complex workflows, it's easy to get lost in logs, IDs, and dashboards. One of our community members recently shared a brilliant use case that shows how LikeC4 can make sense of it all - visually, clearly, and in real time. > _"My scheduling system defines ~1700 jobs, some of which are combined into complex workflows. The scheduler lacks visualization, so I used LikeC4 to generate interactive diagrams - live and linked to our systems."_ ## From Schedules to Systems Diagrams Guys shared their setup, which is clean and powerful: - A **Python script** pulls job/workflow definitions from a scheduler's REST API. - These are turned into **LikeC4 models and views**, automatically. - A WebSocket stream delivers *live status updates*: running, successful, failed, deactivated. - The script updates the model on the fly, likec4 picks up the changes and with HMR (hot module replacement) the diagram is updated in real time. - Each element in the diagram links directly back to the scheduler software UI. _**The result?**_ A living, breathing map of workflows and job states - with errors, dependencies, and critical paths clearly visible at a glance. > _"We can immediately see where errors are, and jump straight from the graph to the job in our scheduler UI. This helps us immensely."_
![Real-time visualization of scheduled jobs and workflows](./realtime-visualization.png) ## What's Next? Visualizing More than Just Jobs This setup is already saving time for the team and reducing cognitive load. But their roadmap is even more exciting: - **External dependencies:** Highlight which jobs interact with FTP servers, external APIs, or file systems. - **Data lineage:** Track which jobs feed data into others, forming real-time pipelines. - **Ownership & responsibility:** Annotate nodes with responsible teams or service owners. - **Audit trails:** Show historical changes in job structures and dependencies over time. - **Incident response:** Use live job status overlays for real-time troubleshooting dashboards. ## Build Your Own Visual Operations Layer The power of LikeC4 lies in turning abstract system definitions into structured, visual models that update in real time. This isn't just architecture documentation - it's operational visibility. If you're managing workflow and jobs, Airflow DAGs, CI pipelines, or microservices with unpredictable interactions - LikeC4 might be the visibility layer you didn't know you needed. # LikeC4 CLI import { PackageManagers } from 'starlight-package-managers';

![NPM Version](https://img.shields.io/npm/v/likec4) ![NPM Downloads](https://img.shields.io/npm/dm/likec4)

The `likec4` CLI is a tool for various operations and automation, such as: - Start development server to preview diagrams (with hot-reload) - Build a static website for sharing and embedding diagrams - Export to PNG, Mermaid, Dot, D2 - Generate [source code artifacts](/tooling/code-generation/react/): - React components - Web Components - Typed data ## Install ### Local installation If you're using it in an npm project, install it as a development dependency: You can reference it directly in the `package.json#scripts` object: ```json5 { scripts: { dev: 'likec4 dev ...', build: 'likec4 build ...' } } ``` ### Global installation To fetch and execute a package binary, without installing it as a dependency: If you want to use it in any arbitrary project without [`npx`](https://docs.npmjs.com/cli/v10/commands/npx), install it globally: ```sh npm install --global likec4 # Then, you can call `likec4` directly: likec4 [command] ``` ## Usage Almost all commands have a `--help` option and provide usage examples. ```sh likec4 build -h likec4 gen react -h ``` ### Preview diagrams In a folder with LikeC4 sources: ```sh likec4 serve # Aliases: likec4 start likec4 dev ``` This recursively searches for `*.c4`, `*.likec4` files in current folder, parses and serves diagrams via local web server. Any change in the sources triggers hot update in the browser immediately. :::tip You can start the process in a separate terminal window and keep it running while you're editing model in editor, or even serve multiple projects at once. ::: ### Build static website Build a single HTML with diagrams, ready to be deployed or embedded into your website. When you deployed the website, you can use "Share" button and get a link to a specific diagram. The resulting website is strictly bound to the given base path (`/` by default). A relocatable app can be built with `--base "./"`. ```sh likec4 build -o ./dist ``` | Option | Description | | ----------------------- | ----------------------------------------------------------------------------------------------------- | | `--output` | Output directory | | `--base, --base-url` | Base URL the app is being served from, e.g. "/", "/pages/" or "./" for a relocatable app | | `--use-hash-history` | Hash-based navigation, e.g. "/#/view" instead of "/view" | | `--use-dot` | Use local binaries of Graphviz ("dot") instead of bundled WASM | | `--webcomponent-prefix` | Prefix for web components, e.g. "c4" generates `` | | `--title` | Base title of the app pages (default is "LikeC4") | | `--output-single-file` | Generates a single self-contained HTML file | :::note Internally, CLI uses Vite to build the website, and `likec4 build` calls `vite build`. Vite [deploy documentation](https://vitejs.dev/guide/static-deploy.html) may also help you. Repository [likec4/template](https://github.com/likec4/template) demonstrates how to deploy likec4 website to github pages. ::: There is also a supplementary command to preview the build: ```sh likec4 preview -o ./dist ``` For example, this command can be used on CI, to compare diagrams with ones from the previous/main build. ### Export to PNG ```sh likec4 export png -o ./assets ``` This command starts local web server and uses Playwright to take screenshots. If you plan to use it on CI, refer to [Playwright documentation](https://playwright.dev/docs/ci) for details or consider [LikeC4 GitHub Actions](/tooling/github) :::note Export to PNG requires Playwright. You will be prompted with a command to install if it's not found. ::: ### Export to JSON ```sh likec4 export json -o dump.json ``` ### Generate Mermaid, Dot, D2, PlantUml Via codegen: ```sh likec4 gen mmd likec4 gen mermaid likec4 gen dot likec4 gen d2 likec4 gen plantuml ``` ### Validate ```sh likec4 validate ``` This command checks for: - syntax errors - layout drift (outdated manual layout) If any error is found the command ends with non-zero return code. # LikeC4 Docker import { PackageManagers } from 'starlight-package-managers'; LikeC4 Docker image is a self-contained environment for running LikeC4 commands and can be used as a drop-in replacement for `likec4` CLI. It is hosted in [GitHub Container Registry](https://github.com/likec4/likec4/pkgs/container/likec4) and [Docker Hub](https://hub.docker.com/r/likec4/likec4) and includes: - Node.js (22.x) - Graphviz (built from sources of the [latest release](https://gitlab.com/graphviz/graphviz/-/releases)) - Playwright (latest) - LikeC4 CLI (latest) You can find the [Dockerfile](https://github.com/likec4/likec4/blob/main/Dockerfile) in the repository. ```sh copy title="Run any CLI command" # Example: Help for export command docker run --rm -t likec4/likec4 export png -h ``` ```sh copy title="Run any CLI command" # Example: Help for export command docker run --rm -t ghcr.io/likec4/likec4 export png -h ``` ## Usage To work with the container you need to mount the folder with LikeC4 sources to `/data` directory (it is the default working directory, but you can change it and use any other you prefer). ### Start local web server ```sh copy title="Start local web server" # mount LikeC4 sources to /data: -v $(pwd):/data # publish ports: -p 5173:5173 # (optional) for realtime updates: -p 24678:24678 # (optional) use init process to correctly handle signals (eg Ctrl+C): --init # (optional) enable color output: -t docker run --rm \ -v $PWD:/data \ --init \ -t \ -p 5173:5173 \ -p 24678:24678 \ -e CHOKIDAR_USEPOLLING=1 \ -e CHOKIDAR_INTERVAL=200 \ likec4/likec4 \ start ``` :::note By default LikeC4 Docker sets `--use-dot` flag and uses local Graphviz binaries instead of bundled WASM (as it has [memory issues](https://github.com/likec4/likec4/issues?q=Memory%20type:Bug)). You can override it with `--no-use-dot` flag. ::: :::caution[Running the Local Web Server on Windows] When using Windows with the Docker-based preview, updates to the diagrams/ file system are not reflected in the running container when files are hosted within a Windows-based file system (i.e. on a drive such as `C:\`). This is a [known issue](https://github.com/microsoft/WSL/issues/4739) with file system notifications for Linux applications using WSL. **Workaround**: Enable polling for file changes by adding the following environment variables to the `docker run` command: ```diff lang="sh" copy title="Start local web server with WSL workaround" docker run --rm \ -v $PWD:/data \ --init \ -t \ -p 5173:5173 \ -p 24678:24678 \ + -e CHOKIDAR_USEPOLLING=1 \ # Enable polling support + -e CHOKIDAR_INTERVAL=200 \ # Adjust timing to your needs likec4/likec4 \ start ``` **Note**: If your diagrams are hosted within a WSL-based filesystem (e.g. /home/user/...), then file system notifications should work as expected. ::: ### Build static website ```sh copy title="Build static website" docker run -v $PWD:/data likec4/likec4 build -o dist ``` ### Export to PNG ```sh copy title="Export to PNG" docker run -v $PWD:/data likec4/likec4 export png --output assets --theme dark ``` # Editors ## VSCode LikeC4 has official [extension for VSCode](https://marketplace.visualstudio.com/items?itemName=likec4.likec4-vscode) - open-source and available on [GitHub](https://github.com/likec4/likec4/tree/main/packages/vscode). The extension provides: - Validation and error reporting - Semantic syntax highlighting - Live Previews (and editing) - Code completion and navigation - Resolve references (like `find all references`, `go to definition` .. ) - "Safe" renames - Hover information - [MCP Server](/tooling/mcp) Extension is universal and can run in the browser. Try [example-cloud-system](https://github.dev/likec4/example-cloud-system) with: ## Neovim LikeC4 has a Neovim plugin for syntax highlighting and LSP integration with code navigation and completion. The plugin supports: - Auto start of the LikeC4 language server for files with .c4 extension - Validation and error reporting - Semantic syntax highlighting - Code completion and navigation - Resolve references (like `find all references`, `go to definition` .. ) - "Safe" renames (do not to forget to write your buffers) - Hover information - Live Previews (and editing) likec4.nvim is available in a separate repository on GitHub - [likec4/likec4.nvim](https://github.com/likec4/likec4.nvim). Installation: ```lua { 'likec4/likec4.nvim', build = 'npm install -g @likec4/language-server' } ``` ## JetBrains IDEs LikeC4 has a JetBrains plugin for syntax highlighting and LSP integration with code navigation and completion. The plugin is available in the [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/26619-likec4-lsp-support) and GitHub - [likec4/jetbrains-plugin](https://github.com/likec4/jetbrains-plugin). # GitHub Actions ![GitHub release](https://img.shields.io/github/release/likec4/actions.svg) This action wraps [CLI](/tooling/cli) as a GitHub Action. ### Usage #### Build website ```yaml steps: - uses: actions/checkout@v4 - name: ⚙️ build uses: likec4/actions@v1 with: action: build path: src/likec4 output: dist base: /baseurl/ - name: upload artifacts uses: actions/upload-artifact@v3 with: name: likec4 path: dist ``` #### Export diagrams to PNG ```yaml steps: - name: export diagrams uses: likec4/actions@v1 with: export: png path: src/likec4 output: out/images use-dot-bin: 'true' ``` #### Code generation ```yaml steps: - name: code generation uses: likec4/actions@v1 with: codegen: react output: __generated__/likec4.jsx ``` ### Inputs | Name | Description | | ------------- | ----------------------------------------------------------------------------------------------------- | | `action` | Action to perform (`build` / `export` / `codegen`) | | `export` | Can be used instead of `action: export` | | `codegen` | Can be used instead of `action: codegen`, same values as in [cli](https://likec4.dev/docs/tools/cli/) | | `path` | Path in repository to likec4 sources, root otherwise | | `output` | Output directory/file | | `base` | Custom baseUrl for website | | `use-dot-bin` | if `'true'` will use `dot` binary of graphviz | > All inputs are optional. > By default CLI builds a website to `dist` directory. # MCP Server LikeC4 MCP Server provides knowledge of your LikeC4 model to LLMs. This enables you to query your model in natural language: > _"Lookup LikeC4 model and list all incoming relationships of the backend api"_ > _"What nested elements of the 'Backend' have relations with the legacy api"_ > _"List all elements tagged legacy from team1 project"_ > _"What technologies are used for ui (consider all elements with browser shape)"_ > _"Export to CSV all relationships between Backend and Amazon SQS"_ ## Usage Three options are available: - Use extension's built-in MCP Server - Use `likec4 mcp` CLI - Use `@likec4/mcp` package ### Using extension When [LikeC4 extension](/tooling/editors/) is installed, MCP Server can be enabled from the extension settings. To configure MCP Server: Create `.vscode/mcp.json`: ```json { "servers": { "likec4": { "type": "sse", "url": "http://localhost:33335/mcp" } } } ``` Create `.cursor/mcp.json`: ```json { "mcpServers": { "likec4": { "url": "http://localhost:33335/mcp" } } } ``` See [Windsurf documentation](https://docs.windsurf.com/windsurf/mcp) for details: ```json { "mcpServers": { "likec4": { "serverUrl": "http://localhost:33335/mcp" } } } ``` You can change port in the settings. :::caution The MCP server is only available when the extension is active. The extension activates automatically once you open any LikeC4 source file. If it doesn't show up, try refreshing the list of available MCP servers in your editor ::: ### Using CLI If you have installed [`likec4`](/tooling/cli) CLI, you can start MCP server with `stdio` transport: ```sh likec4 mcp # or likec4 mcp --stdio ``` Start MCP server with `http` transport on port `33335` (default) at `./src` folder: ```sh likec4 mcp --http ./src ``` Start MCP server with `http` transport on port `1234`: ```sh likec4 mcp -p 1234 ``` ### Using `@likec4/mcp` package Example configuration: ```json { "mcpServers": { "likec4": { "command": "npx", "args": [ "-y", "@likec4/mcp" ], "env": { "LIKEC4_WORKSPACE": "${workspaceFolder}" } } } } ``` This package starts MCP server using [`stdio`](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio) transport. If `LIKEC4_WORKSPACE` environment variable is not set, the current directory will be used as workspace. :::tip You can also use `@likec4/mcp` package as CLI (this package is smaller than `likec4`): ```sh npm install -g @likec4/mcp # Start MCP server with streamable http transport likec4-mcp-server --http --port 1234 /path/to/workspace # See available options likec4-mcp-server -h ``` Disable watch mode with `--no-watch` to consume less resources, if you have static workspace. ::: Check out [README](https://github.com/likec4/likec4/blob/main/packages/mcp/README.md) for more details. # LikeC4 API import { PackageManagers } from 'starlight-package-managers'; You can access and traverse your architecture model programmatically using the LikeC4 Model API. Ensure you have `likec4` in your dependencies: ## Usage You can initiate LikeC4 API from a directory with source files or from a string with DSL source. ### From workspace Recursively search and parse source files: ```ts import { LikeC4 } from 'likec4' const likec4 = await LikeC4.fromWorkspace('/path/to/workspace') ``` Method also accepts options: | Property | Description | | -----------------| --------------------------------------------------------------------------------------------------- | | `printErrors` | if model is invalid, errors are reported to the logger (default `true`) | | `throwIfInvalid` | return rejected promise if model is invalid (default `false`) | | `logger` | Whenever to use `default` (console), `vite` logger or your custom implementation
Disable with `false` | | `graphviz` | `wasm` (default) or `binary` - use local binaries of Graphviz ("dot") or bundled WASM | | `watch` | Whether to watch for changes in the workspace. (default `false`) | | `mcp` | Whether to start MCP server.
- `false` - do not start MCP server (default)
- `"stdio"` - use stdio transport,
- `{"port": number}` - use http transport on specified port | :::tip Take a look at [Validate your model](/guides/validate-your-model) for examples of how to enforce custom rules on your model with the LikeC4 API ::: ### From source Parse from the string: ```ts import { LikeC4 } from "likec4" const likec4 = await LikeC4.fromSource(` specification { element system element user } model { customer = user 'Customer' cloud = system 'System' } views { view index { include * } } `) ``` ### Dispose If you initialized LikeC4 with `watch` mode or enabled MCP server, you should dispose it: ```ts import { LikeC4 } from 'likec4' const likec4 = await LikeC4.fromWorkspace('/path/to/workspace', { watch: true, mcp: { port: 33335 }, }) // Cleanup resources await likec4.dispose() ``` LikeC4 is automatically disposed with `await using` declaration: ```ts import { LikeC4 } from 'likec4' async function() { await using likec4 = await LikeC4.fromWorkspace('/path/to/workspace', { watch: true, mcp: { port: 33335 }, }) // ... // likec4 is disposed automatically } ``` ## API When the model is initialized, you can use the following methods to query and traverse it. Two types of model (with similar API): - **LikeC4Model.Computed** - includes computed views (from predicates), fast, synchronous, enough to traverse but not ready for rendering. - **LikeC4Model.Layouted** - extends computed model with layout data (dimensions, positions), that is needed for rendering. :::tip Low-level API is available from [`@likec4/core`](https://github.com/likec4/likec4/blob/main/packages/core/README.md). ::: ### Example ```ts import { LikeC4 } from "likec4" const likec4 = await LikeC4.fromSource(`....`) // Validation errors console.log(likec4.getErrors()) // Traverse the model const model = likec4.computedModel() // Get elements of some kind const elements = model.elementsOfKind('kind1') // Use where operator to filter elements: // kind is 'kind1' and (tag is 'tag2' or tag is not 'tag3') const elements = model.elementsWhere({ and: [ { kind: 'kind1' }, { or: [ { tag: 'tag2' }, { tag: { neq: 'tag3', }, }, ], }, ], }) // Get views that include the element model .element('cloud.backend.api') .views() // Get source elements of incoming relationships (filter by tags) model .element('cloud.backend.api') .incoming() // relationships incoming to the element .filter(r => r.isTagged('http')) // filter by tags .map(r => r.source) // get source elements ``` To get layouted model: ```ts import { LikeC4 } from "likec4" const likec4 = await LikeC4.fromSource(`....`) const model = await likec4.layoutedModel() const diagram = model.view('index') // Working with metadata (including array values) const element = model.element('cloud.backend.api') // Get all metadata const allMetadata = element.getMetadata() console.log(allMetadata) // Output: { version: '3.2.1', tags: ['backend', 'gateway'], regions: ['us-east-1', 'eu-west-1'] } // Get specific metadata field (could be string or string[]) const tags = element.getMetadata('tags') if (Array.isArray(tags)) { console.log(`Element has ${tags.length} tags: ${tags.join(', ')}`) } else if (tags) { console.log(`Element has single tag: ${tags}`) } // Filter elements by array metadata values const elementsWithBackendTag = model.elements().filter(el => { const tags = el.getMetadata('tags') return Array.isArray(tags) ? tags.includes('backend') : tags === 'backend' }) ``` ### LikeC4Model :::tip It is possible to generate Typed API from your model, see [Code generation](/tooling/code-generation/model/) ::: Model API provides methods to query and traverse the whole model. ```ts interface LikeC4Model { /** * Returns the root elements of the model. */ roots(): Element[]; /** * Returns all elements in the model. */ elements(): Element[]; /** * Returns a specific element by its FQN. */ element(id: Fqn): Element; /** * Returns all relationships in the model. */ relationships(): Relationship[]; /** * Returns a specific relationship by its ID. */ relationship(id: RelationID): Relationship; /** * Returns all views in the model. */ views(): ReadonlyArray; /** * Returns a specific view by its ID. */ view(viewId: ViewID): LikeC4ViewModel; /** * Returns the parent element of given element. * @see ancestors */ parent(element: ElementOrFqn): Element | null; /** * Get all children of the element (only direct children), * @see descendants */ children(element: ElementOrFqn): Element[]; /** * Get all sibling (i.e. same parent) */ siblings(element: ElementOrFqn): Element[]; /** * Get all ancestor elements (i.e. parent, parent’s parent, etc.) * (from closest to root) */ ancestors(element: ElementOrFqn): Element[]; /** * Get all descendant elements (i.e. children, children’s children, etc.) */ descendants(element: ElementOrFqn): Element[]; /** * Incoming relationships to the element and its descendants * @see incomers */ incoming(element: ElementOrFqn, filter?: 'all' | 'direct' | 'to-descendants'): Relationship[]; /** * Source elements of incoming relationships */ incomers(element: ElementOrFqn, filter?: 'all' | 'direct' | 'to-descendants'): Element[]; /** * Outgoing relationships from the element and its descendants * @see outgoers */ outgoing(element: ElementOrFqn, filter?: 'all' | 'direct' | 'from-descendants'): Relationship[]; /** * Target elements of outgoing relationships */ outgoers(element: ElementOrFqn, filter?: 'all' | 'direct' | 'from-descendants'): Element[]; } ``` Check sources for methods - [LikeC4Model](https://github.com/likec4/likec4/blob/main/packages/core/src/model/LikeC4Model.ts) ### Working with Element Metadata Elements can have metadata with both single string values and string arrays. The API provides convenient methods to access and work with this metadata: ```ts import { LikeC4 } from "likec4" const likec4 = await LikeC4.fromSource(` specification { element service element application } model { api = service 'API Gateway' { metadata { version '3.2.1' maintainer 'Platform Team' tags ['backend', 'gateway', 'microservice'] regions ['us-east-1', 'eu-west-1'] critical true } } frontend = application 'Frontend' { metadata { framework 'React' features ['auth', 'dashboard', 'reports'] team_members ['alice', 'bob', 'carol'] release_branch 'main' } } } `) const model = likec4.computedModel() // Get element and access metadata const api = model.element('api') // Check if element has any metadata if (api.hasMetadata()) { console.log('API has metadata') // Get all metadata as an object const metadata = api.getMetadata() console.log('All metadata:', metadata) // Get specific metadata fields const version = api.getMetadata('version') // string: '3.2.1' const tags = api.getMetadata('tags') // string[]: ['backend', 'gateway', 'microservice'] const regions = api.getMetadata('regions') // string[]: ['us-east-1', 'eu-west-1'] // Handle array metadata values if (Array.isArray(tags)) { console.log(`API has ${tags.length} tags:`) tags.forEach(tag => console.log(` - ${tag}`)) // Check if specific value exists in array if (tags.includes('backend')) { console.log('API is tagged as backend service') } } // Handle mixed metadata types const handleMetadataValue = (key: string, value: string | string[] | undefined) => { if (Array.isArray(value)) { return `${key}: [${value.join(', ')}]` } else if (value) { return `${key}: ${value}` } return `${key}: undefined` } console.log(handleMetadataValue('version', version)) console.log(handleMetadataValue('tags', tags)) console.log(handleMetadataValue('regions', regions)) } // Advanced filtering using metadata const backendServices = model.elements() .filter(element => { const tags = element.getMetadata('tags') return Array.isArray(tags) ? tags.includes('backend') : tags === 'backend' }) const multiRegionServices = model.elements() .filter(element => { const regions = element.getMetadata('regions') return Array.isArray(regions) && regions.length > 1 }) // Group elements by metadata values const elementsByFramework = new Map() for (const element of model.elements()) { const framework = element.getMetadata('framework') if (typeof framework === 'string') { if (!elementsByFramework.has(framework)) { elementsByFramework.set(framework, []) } elementsByFramework.get(framework)!.push(element) } } // Collect all unique tags from all elements const allTags = new Set() for (const element of model.elements()) { const tags = element.getMetadata('tags') if (Array.isArray(tags)) { tags.forEach(tag => allTags.add(tag)) } else if (typeof tags === 'string') { allTags.add(tags) } } console.log('All unique tags:', Array.from(allTags).sort()) ``` :::note[Metadata Merging with Extends] When using [`extend`](/dsl/extend/) to add metadata to elements, duplicate metadata keys are automatically merged: - String + String = Array (if different values) - String + Array = Array (merged) - Array + Array = Array (merged) - Duplicate values are automatically de-duplicated - Single-value arrays are converted back to strings This allows you to progressively build up metadata across multiple files. See the [extend documentation](/dsl/extend/#metadata-merging) for more details. ::: ### LikeC4DeploymentModel API provides methods to query and traverse deployment model. ```ts import { LikeC4 } from "likec4" const likec4 = await LikeC4.fromSource(`....`) const model = likec4.computedModel() // Get deployment model const deployment = model.deployment // Get elements of some kind for (const instance of deployment.instancesOf('cloud.backend.api')) { // ... } ``` ### LikeC4ViewModel View model API provides methods to query and traverse elements and relationships that are included in the view. ```ts import { LikeC4 } from "likec4" const likec4 = await LikeC4.fromSource(`....`) const model = likec4.computedModel() for (const view of model.views()) { if (view.isDynamicView()) { // ... } } ``` ## Model Builder Type-safe builder available from `@likec4/core/builder` (and `likec4/model/builder`). Builder can be used to create model programmatically and supports two styles: ```ts import { Builder } from "@likec4/core/builder" const m = Builder .specification({ 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( // code completion for predicates $include('* -> cloud.**'), $style('cloud.ui', { color: 'red' }), ), ) ) .toLikeC4Model() ``` ```ts import { Builder } from "@likec4/core/builder" // Get composition functions for given specification const { model: { model, actor, system, component, rel, relTo, }, views: { view, viewOf, views, $include, $style, }, builder, } = Builder.forSpecification({ elements: { actor: { style: { shape: 'person', }, }, system: {}, component: {}, }, relationships: { likes: {}, }, tags: ['tag1', 'tag2', 'tag1'], }) const b1 = builder.with( model( actor('alice'), actor('bob'), rel('alice', 'bob', { tags: ['tag1'], kind: 'likes', }), system('cloud', { tags: ['tag1', 'tag2'] }).with( component('backend').with( component('api'), component('db'), rel('cloud.backend.api', 'cloud.backend.db') ), component('frontend').with( relTo('cloud.backend.api') ), ), ) ) const b2 = b1.with( views( view('index', 'Index').with( $include('cloud.*'), ), viewOf('ui', 'cloud.ui').with( $include('* -> cloud.**'), $style('cloud.ui', { color: 'red' }), ), ) ) .toLikeC4Model() ``` You can mix both styles, depending on your preference and use cases. :::tip Check unit tests in our repository for examples: - Builder-style1 - Builder-style2 ::: # React Components import { PackageManagers } from 'starlight-package-managers'

![NPM Version](https://img.shields.io/npm/v/likec4)

LikeC4 React library is available to embed diagrams into your applications. Although you can use it directly, consider [Vite Plugin](/tooling/vite-plugin/) or [CLI](/tooling/code-generation/react/) for smoother developer experience. ## Usage You must have `react` and `react-dom` installed. Add [`@likec4/core`](https://www.npmjs.com/package/%40likec4%2Fcore) and [`@likec4/diagram`](https://www.npmjs.com/package/%40likec4%2Fdiagram):
LikeC4 React library can be used in two ways. ### 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 Diagram requires instance of `LikeC4Model.Layouted` to render. You need to prepare it and wrap your diagram with`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](/tooling/code-generation/model/): ```sh likec4 codegen model --outfile ./likec4-model.ts ``` Then: ```tsx copy import { LikeC4ModelProvider } from '@likec4/diagram/bundle' // import model from generated file import { likec4model } from './likec4-model.ts' function App() { return ( {/* ... */} ) } ``` It is possible to prepare model from string. See [API usage](/tooling/model-api/#usage): ```tsx copy 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 ( {/* ... */} ) } ``` You can prepare model with [Builder](/tooling/model-api/#model-builder), then layout it with `layoutLikeC4Model`: ```tsx copy collapse={7-52} import { LikeC4ModelProvider } from '@likec4/diagram/bundle' import { Builder } from "@likec4/core/builder" import { layoutLikeC4Model } from "@likec4/layouts" const computedModel = Builder .specification({ 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 it const likec4model = await layoutLikeC4Model(computedModel) function App() { return ( {/* ... */} ) } ``` :::tip If you have `likec4` in your dependencies, you can use: - `likec4/react` instead of `@likec4/diagram/bundle` - `likec4/model` instead of `@likec4/core/model` and `@likec4/core/types` - `likec4/model/builder` instead of `@likec4/core/builder` - `likec4/icons/all` instead of `@likec4/icons/all` ::: #### LikeC4View ```tsx import { LikeC4View, LikeC4ModelProvider } from '@likec4/diagram/bundle' function App() { return ( console.log(nodeId)} /> {/* Possible to have multiple views */} ) } ``` See [LikeC4ViewProps](https://github.com/likec4/likec4/blob/main/packages/diagram/src/bundle/LikeC4View.props.ts) for available props. #### 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`: ```tsx import { ReactLikeC4, LikeC4ModelProvider } from '@likec4/diagram/bundle' function App() { const [viewId, setViewId] = useState('index') return ( ) } ``` #### Hooks Available hooks inside `LikeC4View` or `ReactLikeC4`: ```tsx import { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel, useEnabledFeatures, useCurrentViewId, // XYFlow hooks useXYFlow, useXYStore, useXYStoreApi, // Diagram API useDiagram, // Select from state useDiagramContext } from '@likec4/diagram/bundle' ``` #### Icons If you use built-in icons, install [`@likec4/icons`](https://www.npmjs.com/package/%40likec4%2Ficons) (or use `likec4/icons`): ```tsx 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 moment const Icon = lazy(async () => { const { IconRenderer } = await import('@likec4/icons/all') return { default: IconRenderer } }) const IconRenderer: ElementIconRenderer = (props) => ( ) function App() { return ( {/* Same for ReactLikeC4 */} ) } ``` ### Library If you want to use package as a library with your bundler, you have to take care of CSS. Library uses [Mantine](https://mantine.dev). 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 1. Import all styles ```css @import '@likec4/diagram/styles.css' ``` This includes all styles, including [Mantine](https://mantine.dev) styles. 2. If you are using Mantine ```css @layer reset, base, mantine, xyflow, tokens, recipes, utilities; @import "@mantine/core/styles.layer.css"; @import "@likec4/diagram/styles-min.css"; ``` :::caution Order of layers is important, make sure `mantine` layer is before `xyflow`, and `xyflow` is before `tokens`. ::: 3. Font.\ LikeC4Diagram uses [`IBM Plex Sans`](https://fontsource.org/fonts/ibm-plex-sans) by default.\ You can bundle it, or import from [fontsource](https://fontsource.org/fonts/ibm-plex-sans), any other CDN or: ```css @import '@likec4/diagram/styles-font.css' ``` You can override the font, this is explained later. #### With PandaCSS Check [PandaCSS](https://panda-css.com) docs for full setup instructions. LikeC4 provides preset.
Configure your `panda.config.ts`: ```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: ```css @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"; ``` #### Usage Same as [ReactLikeC4](#reactlikec4), but import from `@likec4/diagram` and you have to provide instance of `DiagramView`: ```tsx import { LikeC4Diagram, LikeC4ModelProvider, useLikeC4ViewModel } from '@likec4/diagram' function LikeC4View({viewId}: {viewId: string}) { const view = useLikeC4ViewModel(viewId) if (!view) { return <>View not found } return ( ) } function App() { return ( ) } ``` ## Customization You can render any component inside `LikeC4Diagram`\ (or `LikeC4View`/`ReactLikeC4` if you are using bundle): ```tsx import { LikeC4Diagram, LikeC4ModelProvider } from '@likec4/diagram' import { Panel, ViewportPortal } from '@xyflow/react' function App() { return ( {/* You can use components from xyflow */}

Your component as a panel

Check examples
This div is positioned at [100, 100] on the diagram canvas
) } ``` ### 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.ts](https://github.com/likec4/likec4/blob/main/packages/diagram/src/custom/customNodes.ts) for examples. ```tsx 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 }) => ( {/* Add extra buttons */} , onClick: () => console.log('extra'), }, ]} /> {/* Add extra info */}
{nodeModel.element.getMetadata('your-attr')}
)), } function App() { return ( ) } ``` You can also use [hooks](/tooling/react/#hooks) to access the model and diagram API. ### Custom styles LikeC4Diagram uses [PandaCSS](https://panda-css.com) for styling. You can use it to customize the styles. TODO: add example # LikeC4 Vite Plugin import { PackageManagers } from 'starlight-package-managers'

![NPM Version](https://img.shields.io/npm/v/likec4) ![NPM Downloads](https://img.shields.io/npm/dm/likec4)

LikeC4 Vite Plugin allows you to embed views directly, without any pre-build/generate steps. The plugin automatically generates all the necessary code to render the views in your application, with Hot Module Replacement (HMR) supported. This is useful for building documentation, tutorials, or any other application where you want to include diagrams. ## Guide
1. ### Create Vite project To get started, we will need to create a new Vite project using react-ts template.

2. ### Install LikeC4 Add `likec4` dependency:

3. ### Configure Vite Add LikeC4 plugin to vite config: ```diff lang="ts" // vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' + import { LikeC4VitePlugin } from 'likec4/vite-plugin' export default defineConfig({ plugins: [ react(), + LikeC4VitePlugin(), ], }) ```

4. ### Add type references Add types reference to the `vite-env.d.ts` file (or create new one, like `src/likec4.d.ts`) ```diff lang="ts" {3} // src/vite-env.d.ts /// /// ``` Another option is to add to the `tsconfig.json`: ```json // tsconfig.json { "compilerOptions": { "types": [ "likec4/vite-plugin-modules" ] } } ```

5. ### Add LikeC4 model Create `src/tutorial.c4` and copy the following model from tutorial: ```likec4 showLineNumbers copy collapse={14-57} //src/tutorial.c4 // Tutorial - https://likec4.dev/tutorial/ specification { element actor element system element component } model { customer = actor 'Customer' { description 'The regular customer of the system' } saas = system 'Our SaaS' { component ui 'Frontend' { description 'Nextjs application, hosted on Vercel' style { icon tech:nextjs shape browser } } component backend 'Backend Services' { description ' Implements business logic and exposes as REST API ' } // UI requests data from the Backend ui -> backend 'fetches via HTTPS' } // Customer uses the UI customer -> ui 'opens in browser' customer -> saas 'enjoys our product' } views { view index { title 'Landscape view' include * } view saas of saas { include * style * { opacity 25% } style customer { color muted } } } ```

6. ### Use LikeC4 view in your app Change the `src/main.tsx` file and import LikeC4 view from the `likec4:react` module: ```tsx // src/main.tsx import { createRoot } from 'react-dom/client' import { LikeC4View } from 'likec4:react' createRoot(document.getElementById('root')!).render( ) ```

7. ### Start vite dev server Open the browser and navigate to `http://localhost:5173/`. You should see the LikeC4 diagram rendered in your app.
## Plugin ### Options | Option | Description | | -----------------| --------------------------------------------------------------------------------------------------- | | `workspace` | directory with source files (defaults to vite root) | | `printErrors` | if model is invalid, errors are reported to the logger (default `true`) | | `throwIfInvalid` | fails with rejected promise if model is invalid (default `false`) | | `graphviz` | `wasm` (default) or `binary` - use local binaries of Graphviz ("dot") or bundled WASM | ### Multi-project workspaces If you have [multiple projects](/dsl/config/multi-projects/) in your workspace: ```tsx "project-a" "project-b" // src/main.tsx // where `project-a` and `project-b` are the names of your projects import { LikeC4View as ProjectA_LikeC4View } from 'likec4:react/project-a' import { LikeC4View as ProjectB_LikeC4View } from 'likec4:react/project-b' const example = () => ( <> ) ```
### Usage with API It is also possible to initiate using [LikeC4 API](/tooling/model-api): ```ts // vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { LikeC4 } from 'likec4' import { LikeC4VitePlugin } from 'likec4/vite-plugin' const { languageServices } = await LikeC4.fromSource(` specification { element system element user } model { customer = user 'Customer' cloud = system 'System' } views { view index { include * } } `) export default defineConfig({ plugins: [ react(), LikeC4VitePlugin({ languageServices, }), ], }) ```
### Virtual modules Other modules are available to get access to the model: ```tsx // For multi-project workspaces import { projects } from 'likec4:projects' // Pick first one (default) import { useLikeC4Views, useLikeC4View } from 'likec4:single-project' // Project by name import { useLikeC4Views, useLikeC4View } from 'likec4:model/project-a' // Other modules import { loadDotSources } from 'likec4:dot' import { mmdSource } from 'likec4:mmd/project-a' ``` Complete list - vite-plugin/modules.d.ts ## Usage Here are some examples of how to use the plugin with different frameworks ### With Astro You can use LikeC4VitePlugin with Astro and Starlight documentation tool as well. Configure Astro: ```js // astro.config.mjs import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import { LikeC4VitePlugin } from 'likec4/vite-plugin' export default defineConfig({ integrations: [ starlight({ title: 'Your architecture docs site', }), ], vite: { plugins: [ LikeC4VitePlugin({}), ], }, }); ``` To use React components, first you need to wrap them in astro components: ```astro // src/components/LikeC4View.astro --- import { LikeC4View as ReactLikeC4View, type LikeC4ViewId } from 'likec4:react'; interface Props { viewId: LikeC4ViewId; } const { viewId } = Astro.props --- ``` Then you can use in markdown: ```mdx // src/content/docs/example.mdx --- title: Welcome to my docs --- import LikeC4View from '../../components/LikeC4View.astro'; ## Introduction This is an example of using LikeC4 in your documentation ```
:::tip Check sources how LikeC4 multi-projects are embedded in this website. ::: :::caution Don't forget to add type references as described [here](/tooling/vite-plugin/#add-type-references). For Astro this is `src/env.d.ts` file. ::: ### With Next.js For Next.js (since it does not use Vite), there is a workaround - [library mode](https://vite.dev/guide/build.html#library-mode): Vite will generate a bundled library with likec4 diagrams, that you can import from your Next.js app. Export everything from `likec4:react`: ```tsx // src/likec4/index.tsx export * from 'likec4:react' ```
Configure Vite: ```ts // vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { LikeC4VitePlugin } from 'likec4/vite-plugin' export default defineConfig({ build: { // Build views to 'lib' directory outDir: 'lib', lib: { entry: 'src/likec4/index.tsx', }, rollupOptions: { // make sure to externalize deps that shouldn't be bundled // to avoid code duplication external: [ 'react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom/client', ], }, }, plugins: [ react(), LikeC4VitePlugin(), ], }) ``` Run `vite build` and import outputs from your Next.js app: ```tsx // pages/index.tsx import { LikeC4View } from '../lib' export default function Home() { return ( ) } ``` You can run `vite build --watch` as a background process to watch for changes in likec4 source files.

:::tip You can use LikeC4 with other frameworks as well: - via [React code generation](/tooling/code-generation/react/) - For non-react-based documentation tools like Docusaurus, see [Web Components](/tooling/code-generation/webcomponent/) ::: # Project config To define a project, create a `likec4.config.json` file in the folder. All files in the folder (and subfolders) will be part of this project: - externals - amazon.c4 - ... - services - service1.c4 - service2.c4 - ... - specification.c4 - **likec4.config.json** ## Configuration The `likec4.config.json` file must have the **name** of the project. ```jsonc { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", // required "title": "Project Title" // optional } ``` The name must be unique if you use [multiple projects](/dsl/config/multi-projects). :::note You can use these names for the config file: - `.likec4rc` - `.likec4.config.json` - `likec4.config.json` LikeC4 interprets any file as JSON5 You can also use these names: - `likec4.config.js` - `likec4.config.mjs` - `likec4.config.ts` - `likec4.config.mts` See [Programmatic config](/dsl/config/programmatic) for more information. ::: ## Exclude files By default, LikeC4 recursively scans in the project folder. You can exclude files by adding an `exclude` array to the config file. ```json { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", "exclude": [ "**/node_modules/**" ] } ``` If no exclude pattern is provided, LikeC4 uses `["**/node_modules/**"]` as default. The exclude pattern is the same as the one used by [picomatch](https://github.com/micromatch/picomatch). ## Image Aliases When using local images in your LikeC4 model, you can create aliases for the folder your images are in to make them more readable and the files more transportable. Use the `likec4.config.json` to add an `imageAliases` field: ```json { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", "imageAliases": { "@": "./images", "@root": "../../some-more-images" } } ``` You can then use the alias in your model: ```likec4 title="example" // ./amazon.c4 model { serviceA = service { icon: @/service-a.png } serviceB = service { icon: @root/service-b.png } } // ./externals/externals.c4 model { serviceC = service { icon: @/service-c.png } } ``` - some-more-images - service-b.png - ... - docs - project - images - service-a.png - service-c.png - ... - externals - externals.c4 - likec4.config.json - amazon.c4 - ... :::note In the example above the `externals.c4` is a nested file, but the aliasing works based on the project root. ::: ### Naming Rules When using image aliases, keep the following rules in mind: - Aliases must start with `@` and can include letters, numbers, and underscores. - The alias must be unique within the project. - The alias can point to a relative or absolute path. ### Defaults When no LikeC4 configuration file is found, or when no `imageAliases` field is found, LikeC4 uses the following defaults: ```json { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", "imageAliases": { "@": "./images", } } ``` Simply override the `@` to change the default location. ```json { "imageAliases": { "@": "./my-images", } } ``` ## Styles customization LikeC4 provides advanced style customization capabilities. ### Theme overrides You can override default theme colors and sizes by adding a `styles.theme` section to the config file. Each definition can be either a CSS value or a detailed object specifying color for specific parts. Example of simple color definition: ```jsonc { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", "styles": { "theme": { "colors": { "primary": "#FF6B6B", "secondary": "rgba(37,99,235,1)", } }, } } ``` Example of detailed color definition: ```jsonc { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", "styles": { "theme": { "colors": { "muted": { "elements": { "fill": "#2563eb", // Background color "stroke": "#1d4ed8", // Border color "hiContrast": "#ffffff", // Title text color "loContrast": "#e2e8f0" // Description text color }, "relationships": { "line": "#1d4ed8", // Line color "label": "#ffffff", // Label text color "labelBg": "rgba(37,99,235,0.1)" // Label background color } }, // You can give detailed definitions for a custom color in your specification "custom-color-from-your-spec": { // ... } } }, } } ``` You can override sizes used in LikeC4: ```jsonc { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", "styles": { "theme": { "sizes": { "md": { "width": 200, "height": 200 }, "lg": { "width": 300, "height": 300 } } } } } ``` ### Default styles You can override default values for style properties by adding a `styles.defaults` section to the config file. These values will be applied to all elements and relationships, unless properties are explicitly defined in the specification. ```jsonc { "$schema": "https://likec4.dev/schemas/config.json", "name": "project-name", "styles": { "defaults": { // Defaults for all elements "border": "dashed", "opacity": 100, "size": "md", "color": "primary", // theme color name // Defaults for groups "group": { "color": "primary", "opacity": 10, "border": "dashed" }, // Defaults for relationships "relationship": { "color": "gray", "line": "dashed", "arrow": "normal" } } } } ``` # Multi-projects Sometimes you may want to split your LikeC4 model into multiple ones, based on domains, teams, or any other criteria. You can do this by creating multiple projects in your workspace and linking them together. You can also use this feature to share your model with other teams or projects. Create `likec4.config.json` files in the folders you want to be projects: - cloud - **likec4.config.json** - service1.c4 - service2.c4 - ... - externals - **likec4.config.json** - amazon.c4 - ... Projects can be nested. In this case, files from the nested project are not part of the parent project. - cloud - likec4.config.json - service1.c4 - service2.c4 - nested - likec4.config.json - service3.c4 // this will be part of the 'nested' project, not the 'cloud' project ## Import elements You can import elements from other projects by using the `import` keyword. ```likec4 import { serviceA } from 'projectA' model { serviceB = service { -> serviceA.api 'calls serviceA' } } ``` ## Share specification At the moment, the only way to share specification (or any other parts of the model) is to use symlinks: - shared - specs.c4 - cloud - specs.c4 // -> ../shared/specs.c4 - likec4.config.json - ... - externals - specs.c4 // -> ../shared/specs.c4 - likec4.config.json - ... # TypeScript/JavaScript Config import { PackageManagers } from 'starlight-package-managers' :::caution This feature is experimental and not well tested with IDEs. If you find any issues, please report them to [GitHub](https://github.com/likec4/likec4/issues). ::: You can configure your projects programmatically with TypeScript or JavaScript. The config filename can be any of the following: - `likec4.config.js` - `likec4.config.mjs` - `likec4.config.ts` - `likec4.config.mts` Example: ```ts // likec4.config.ts import { defineConfig } from 'likec4/config' export default defineConfig({ name: 'my-project', title: 'My Project', exclude: ["**/node_modules/**", "**/.cache/**"], imageAliases: { "@": "./images", "@root": "../../some-more-images" } }) ``` :::tip You can also use smaller package `@likec4/config`: ```ts import { defineConfig } from '@likec4/config' export default defineConfig({ name: 'my-project', title: 'My Project' }) ``` ::: ## Custom Generators LikeC4 CLI has a [`generate`](/tooling/cli/#generate-mermaid-dot-d2-plantuml) command to generate files from your model. You can define custom generators in your project config: ```ts // likec4.config.ts import { defineConfig } from 'likec4/config' export default defineConfig({ name: 'my-project', title: 'My Project', generators: { 'hello': async ({ likec4model, ctx }) => { await ctx.write({ path: 'hello.txt', // relative to the project root content: `Project: ${likec4model.project.id}`, }) }, }, }) ``` Now you can run your generator with [CLI](/tooling/cli): ```bash likec4 gen hello ``` In [multi-project](/dsl/config/multi-projects) workspace use: ```bash likec4 gen hello --project my-project # Other options likec4 gen hello --project my-project --use-dot ``` ### Reusable Generators There is also helper function `defineGenerators` to define reusable generators: ```ts title="example" // shared_generators.ts import { defineGenerators } from 'likec4/config' export default defineGenerators({ 'hello': async ({ likec4model, ctx }) => { await ctx.write({ path: 'hello.txt', // relative to the project root content: `Project: ${likec4model.project.id}`, }) }, }) // likec4.config.ts import { defineConfig } from 'likec4/config' import generators from './shared_generators' export default defineConfig({ name: 'my-project', title: 'My Project', generators, }) ``` ## Styles Customization Same as in [JSON-config](/dsl/config/#styles-customization), you can define styles in TypeScript config: ```ts // likec4.config.ts import { defineConfig } from 'likec4/config' export default defineConfig({ name: 'my-project', title: 'My Project', styles: { // Theme section allows you to override default colors and sizes theme: { colors: { // Simple color definition - automatically generates element/relationship colors primary: "#FF6B6B", secondary: "#4ECDC4", // Detailed color definition with specific values muted: { elements: { fill: "#2563eb", // Background color stroke: "#1d4ed8", // Border color hiContrast: "#ffffff", // Title text color loContrast: "#e2e8f0" // Description text color }, relationships: { line: "#1d4ed8", // Line color label: "#ffffff", // Label text color labelBg: "rgba(37,99,235,0.1)" // Label background color } }, // Give detailed definitions for a color from your specification "custom-color-from-your-spec": { // ... } }, sizes: { // override dimensions md: { width: 200, height: 200 } } }, // Override default values for style properties, // These values will be used if such property is not defined defaults: { border: "dotted", opacity: 100, size: "md", color: "slate", group: { color: "green", opacity: 10, border: "solid" }, relationship: { color: "indigo", line: "solid", arrow: "diamond" } } } }) ``` ### Reusable Styles There is also helper functions `defineStyle`, `defineTheme` and `defineThemeColor`. You can export them from a separate file (or publish as a package, to share with others) and import them in your config. ```ts title="@your-org/likec4-theme" import { defineStyle, defineTheme, defineThemeColor } from 'likec4/config' const theme1 = defineTheme({ colors: { primary: "#FF6B6B", }, }) const theme2 = defineTheme({ colors: { primary: "#4ECDC4", }, }) export default defineStyle({ // Even use environment variable to switch between themes theme: process.env['THEME'] === 'theme2' ? theme2 : theme1, defaults: { // ... } }) ``` And in your config: ```ts // likec4.config.ts import { defineConfig } from 'likec4/config' import styles from '@your-org/likec4-theme' export default defineConfig({ name: 'my-project', title: 'My Project', styles, }) ```
# Deployment Model Deployment Model represents another layer, _physical model_ with its own structure and elements (deployment nodes). It references the [logical model](/dsl/model) and inherits its relationships. ## Specification First, following the [same approach](/dsl/specification/#element-kind), deployment node kinds have to defined within the specification: ```likec4 specification { deploymentNode environment deploymentNode zone deploymentNode kubernetes { // Nodes have same styling options style { color blue icon tech:kubernetes multiple true } } deploymentNode vm { // Common properties for the kind notation 'Virtual Machine' technology 'VMware' } } ``` You define whatever you need to represent your deployment model and your ubiquitous language. ## Deployment nodes The deployment model is a set of nodes, organized in a hierarchical structure: ```likec4 deployment { environment prod { zone eu { zone zone1 { vm vm1 vm vm2 } // You can also use '=' with the name coming first zone2 = zone { vm1 = vm vm2 = vm } } } } ``` Node names must be unique within its container (parent node); same rules as for element names in the logical model. Nodes may have properties just like [logical elements](/dsl/model/#element-properties). ```likec4 deployment { environment prod 'Production' { #live #sla-customer technology 'OpenTofu' summary 'Production environment' description ''' ## Detailed description With **Markdown** support ''' link https://likec4.dev zone eu { title 'EU Region' // ... } } } ``` ### Extend nodes As in the logical model, you can [`extend`](/dsl/extend/#extend-element) deployment nodes: ```likec4 // File: 'deployments/prod.c4' deployment { environment prod } // File: 'deployments/prod/zone-eu.c4' deployment { extend prod { zone eu } } ``` Same [rules](/dsl/extend/#extend-element) apply for extending nodes: - extended node must be referenced by a fully qualified name. - define [additional properties](/dsl/extend/#additional-properties). ## Deployed instances Operator `instanceOf` _“deploys”_ elements from the logical model to deployment nodes: ```likec4 deployment { environment prod { zone eu { zone zone1 { // 'frontend.ui' is a logical element // by default, instance has same name, // i.e. it becomes 'prod.eu.zone1.ui' instanceOf frontend.ui // this becomes 'prod.eu.zone1.api' instanceOf backend.api } zone zone2 { // or use '=' with the name coming first ui = instanceOf frontend.ui // two instances of same element api1 = instanceOf backend.api api2 = instanceOf backend.api } // Deploy to any level, not only leaf nodes // Assume database shared between zones db = instanceOf database } } } ``` Deployed instance inherits properties and styling from the element. It is possible to override: ```likec4 deployment { environment prod { zone eu { db = instanceOf database { title 'Primary DB' technology 'PostgreSQL with streaming replication' icon tech:postgresql style { color red } } } } } ``` ## Deployment relationships Deployment model inherits relationships from the logical model. But also allows to define specific ones: ```likec4 deployment { environment prod { vm vm1 { db = instanceOf database 'Primary DB' } vm vm2 { db = instanceOf database 'Standby DB' } vm2.db -> vm1.db 'replicates' } } ``` As you see, relationship is between same logical element, but different instances. Assume, we don't need this relationship in our logical model, but it makes sense for deployment. Deployment relationships can be "kinded", and have [same properties](/dsl/relationships/#relationship-properties) as logical ones: ```likec4 deployment { environment prod { vm2.db -[streaming]-> vm1.db { #next, #live title 'replicates' description 'Streaming replication' } } } ``` :::tip Relationships can be defined for nested elements of deployed instances: ```likec4 model { component database { component repl_log } } deployment { vm vm1 { db = instanceOf database 'Primary DB' } vm vm2 { db = instanceOf database 'Standby DB' } // 'repl_log' is a nested element of deployed instance vm2.db -> vm1.db.repl_log 'replicates' } ``` ::: :::note Check this GitHub discussion for further development. Feel free to share your ideas. ::: # Deployment views Deployment views allow you to visualize the deployment model, using same approach as [model views](/dsl/views/predicates) — predicates. ## View definition ```likec4 {17-23} deployment { environment prod { zone eu { zone zone1 { instanceOf frontend.ui instanceOf backend.api } zone zone2 { instanceOf frontend.ui instanceOf backend.api } instanceOf database } } } views { deployment view index { title 'Production Deployment' link https://likec4.dev include prod.** // ... } } ``` ## View predicates Deployment views are based on same [predicates](/dsl/views/predicates) as model views. But they refer to deployment nodes and instances. ### Filtering Filtering in deployment views use the same principles as in normal views but takes into account deployment nodes, relations and tags defined in deployment model. When condition on element is checked the following rules are applied: - For a deployment instances tags are combined from tags defined in model and tags defined in deployment model - For a child of deployment instance the tags defined on child in model are used - For a deployment node the tags defined in deployment model are used - For a deployment instances the kind of the model element is used - For a child of deployment instance the kind of this child is used - For a deployment node the kind of this deployment node is used - Tags are not inherited from parent nodes/elements ```likec4 model { element cloud { element frontend { #next -> backend "rel1" } element backend { #next -> db "rel2" } element db } } deployment { environment prod { // Resulting tags: #alpha #alpha zone eu { // Resulting tags: #beta #beta instanceOf frontend { // Resulting tags: #next, #gamma #gamma } instanceOf backend { // Resulting tags: #next, #sigma #sigma } eu -> prod.db "rel3" } instanceOf db { // Resulting tags: #delta #delta } } } views { deployment view some { include prod.eu.frontend -> prod.eu.backend where source.tag is #next // includes relation "rel1" include prod.eu.frontend -> prod.eu.backend where source.tag is #gamma // includes relation "rel1" include prod.eu -> prod.db where source.tag is #beta // includes relation "rel3" include prod.eu -> prod.db where source.tag is #sigma // does not include any relations include eu.* -> prod.db where source.tag is #sigma // includes relations "rel2" } } ```

# Dynamic views Dynamic view describes a particular use-case or scenario, with specific elements and interactions, defined only in the view (without polluting the model). ## Dynamic view definition ```likec4 showLineNumbers copy collapse={1-54} //dynamic-view.c4 specification { element actor { style { shape person } } element system element component } model { customer = actor 'Customer' { description 'Customer of Cloud System' } cloud = system 'Cloud System' { backend = component 'Backend' { description 'Backend services and API' auth = component 'Authentication' api = component 'Backend API' { description 'RESTful API' } api -> auth 'validates bearer token' } ui = component 'Frontend' { description ' All the frontend applications of Cloud System ' style { shape browser } web = component 'Customer Dashboard' { description 'React Application' style { shape browser } } web -> auth web -> api 'requests' } } customer -> web 'opens in browser' } views { dynamic view example { title 'Dynamic View Example' customer -> web 'opens in browser' web -> auth 'updates bearer token if needed' web -> api 'POST request' api -> auth // title is derived from the model api -> api 'process request' // allow self-call // reverse direction, as a response to line 59 web <- api 'returns JSON' // Include elements, that are not participating include cloud, ui, backend style cloud { color muted opacity 0% } } } ``` ### Continuous steps Alternative syntax for describing continuous steps: `A -> B -> C` ```likec4 copy dynamic view example { customer -> web -> api // same as web -> api -> web // same as web <- api } ``` It identifies the backward direction of the step, i.e. `A -> B -> A` is the same as `A -> B; A <- B`. Nested steps are processed as well, i.e. ```likec4 A -> B -> C -> D -> B -> A ``` is the same as ```likec4 A -> B B -> C C -> D D -> B A <- B // is backward ``` ### Parallel steps ```likec4 copy dynamic view parallelexample { title 'Dynamic View Parallel Example' ui -> api parallel { api -> cache api -> db } // or par { api -> cache api -> db } } ``` Nested parallel blocks are not possible - see this discussion ### Navigation Steps can navigate to other dynamic views: ```likec4 copy dynamic view level1 { title 'Highlevel' ui -> api { navigateTo moreDetails } } dynamic view moreDetails { title 'Some details' } ``` ### Notes `notes` can be used to add additional information to the step. It supports Markdown: ```likec4 copy dynamic view stepnotes { title 'Dynamic View Parallel Example' ui -> api { notes ' 🏛️ - Requests data using predefined GraphQL queries 🤖 - Queries regression on CI ' } parallel { api -> cache { // Supports Markdown notes ''' **What it does**: - requests session-scoped data - updates TTL ''' } } } ``` ## Variants Dynamic views support two variants: `diagram` and `sequence`. By default, dynamic views are displayed as diagrams. ### Diagram ![diagram variant](../../../../assets/views/dynamic-variant-diagram.png) ### Sequence Classic sequence diagram: ![sequence variant](../../../../assets/views/dynamic-variant-sequence.png) :::note The sequence variant supports only connections with _leaf_ elements, i.e. elements that do not have any child elements. ::: ## Order of actors The sequence variant allows to set the order of actors with `include` predicate: **Default variant**: ```likec4 copy dynamic view order1 { customer -> web -> auth -> web -> api } ``` Steps define the order of actors. ![diagram sequence-order-1](../../../../assets/views/sequence-order-1.png) **Ordered variant:** ```likec4 copy {9-12} dynamic view order2 { customer -> web -> auth -> web -> api // Strict order include auth, web, api } ``` `include` predicate may define order partially, for the rest order will be derived based on the steps. ![diagram sequence-order-2](../../../../assets/views/sequence-order-2.png) ## Example Browse this example:
# Views LikeC4 is model-based, and views are projections of the model from various perspectives, scopes, and levels of detail, such as: - System/service overviews - Component interactions in specific use cases - Data flows and sequence diagrams LikeC4 does not enforce specific rules, such as a strict number of levels or what should be included; it’s entirely up to you and your context. ## View definition Views are defined in `views` section. They can be named (must be unique) or unnamed (can’t be referenced, but still can be exported): ```likec4 views { // with name view index { } // unnamed view { } } ``` The view’s name is used as an image filename during export and as part of the URL when sharing, so it’s advisable to define one. ### View properties Views can have a `title`, `description`, `tags` and `links`: ```likec4 views { view epic12 { #next, #epic-12 title "Cloud System - Changes in Epic-12" // Description can be Markdown with triple quotes description """ This diagram shows the **high-level** components and interactions. """ link https://my.jira/epic/12 'Epic-12' } } ``` Properties must be defined before [predicates](/dsl/views/predicates/). ## Scoped views A view can be defined for a specific element (`view of ..`). The view will then inherit the [scope](/dsl/references#scope) of that element: :::note You will learn about `include` and `exclude` in the [predicates](/dsl/views/predicates/) section. ::: ```likec4 views { view { include api // ⛔️ Error: 'api' is not found } view of cloud.backend { include api // ✅ This is OK, resolves to 'cloud.backend.api' } view of legacy { include api // ✅ This is OK, resolves to 'legacy.api' } } ``` Additionally, a scoped view becomes the default for the element: ```likec4 views { view { // on click navigates to 'view1', // because it is default for 'cloud.backend' include cloud.backend } view view1 of cloud.backend { include * } } ``` You can define multiple views for the same element, with the default determined by their order. ## Extend views Views can be extended to avoid duplication, to create a "baseline" or, for example, "slides" for a presentation: ```likec4 views { view view1 { include * } view view2 extends view1 { title 'Same as View1, but with more details' style * { color muted } include some.backend } // cascade inheritance view view3 extends view2 { title 'Same as View2, but with more details' include * -> some.backend } } ``` The predicates and style rules of extended views applied after the ones from ancestors. Extended view also inherits the scope: ```likec4 views { view view1 of cloud.backend { title 'Backend components' } view view2 extends view1 { include api // ✅ This is OK, references 'cloud.backend.api' } } ``` # Organize views You can organize views into folders to keep the workspace clean and easy to navigate. ## Create folders To create a folder use `/` in the title. For example, the following view will have the title `Production`, and will be nested under `Deployment` folder: ```likec4 views { view { title 'Deployment / Production' } } ``` On the UI, this view will be displayed as: ![organize views](../../../../assets/views/organize.png) Views can be defined in the same file, but placed in different folders: ```likec4 views { dynamic view { title 'Use Cases / 16.2 Checkout / Checkout flow' } deployment view { title 'Deployments / Staging / Checkout microservice' } } ``` ## Common folder You can specify a common folder for the `views` block. Every view defined inside will be placed under that folder: ```likec4 "'Domain 1 / Subdomain'" // Common folder for all views in the block views 'Domain 1 / Subdomain' { view { // Will be displayed as 'Domain 1 / Subdomain / Landscape' title 'Landscape' } dynamic view { // You can add nested folder // Will be displayed as 'Domain 1 / Subdomain / Use Cases / 16.2 Checkout' title 'Use Cases / 16.2 Checkout' } } ``` # View Predicates Views are not static, they are generated from the model. Any changes in the model are applied immediately and update views. Two types of predicates define what is visible: element and relationship predicates. ## Element predicates Element predicates explicitly define which elements are visible. Each included element brings in its relationships with already visible elements. ```likec4 view { // Only backend is visible include backend // Add frontend to the view // and its relationships with backend include frontend // Add authService to the view // and its relationships with visible (backend and frontend) include authService // Add children of messageBroker, // and their relationships among themselves and visible (backend, frontend and authService) include messageBroker.* // Add all descendants of messageBroker, // and their relationships among themselves and visible (backend, frontend and authService) include messageBroker.** // Exclude emailsQueue and its relationships exclude messageBroker.emailsQueue } ``` ### Combining Predicates can be combined. The following is the same as example above: ```likec4 view { include backend, frontend, authService, messageBroker.** exclude messageBroker.emailsQueue } ``` ### Wildcard Wildcard predicates can be used to reference "everything" (but it differs for scoped/unscoped views). Consider the following model: ```likec4 model { actor customer { -> webApp 'uses in browser via HTTPS' } system cloud { container backend { component api } container ui { component webApp { -> api 'requests data' } } } } views { // Unscoped view - wildcard refers to top-level elements view { include * // Visible top-level elements: customer, cloud // and derived relationship customer -> cloud } // Scoped view - wildcard refers to element and its children view of cloud.ui { include * // Visible: // - cloud.ui // - cloud.ui.webApp // - customer // - relationship customer -> cloud.ui.webApp // - cloud.backend // - cloud.ui.webApp -> cloud.backend, derived from cloud.ui.webApp -> cloud.backend.api } } ``` ### With overrides You can modify element properties specifically for the view: ```likec4 // Include the element and override its properties include cloud.backend with { title 'Backend components' description '...' technology 'Java, Spring' icon tech:java color amber shape browser multiple true } // Include all nested elements, change color and textSize include cloud.* with { color amber textSize small } ``` `with` may be used only within `include`. ### With custom navigation You can define custom navigation and links between views: ```likec4 title="example.c4" view view2 { include * include cloud.backend with { // navigate to 'view3' on click navigateTo view3 } } view view3 { include * include cloud.backend with { // navigate back to 'view2' navigateTo view2 } } ``` ### By element kind or tag ```likec4 // elements by kind include element.kind != system exclude element.kind = container // elements by tag include element.tag != #V2 exclude element.tag = #next ``` :::caution These predicates may be deprecated in the future, please consider [`where`](#filter) operator ::: ### Element Selectors #### Children `.*` The children selector includes element's children and their relationships with visible elements. ```likec4 include cloud.* // Same as include cloud.backend include cloud.ui ``` #### Descendants `.**` The descendants selector includes element's descendants **IF** they have a relationship with visible elements. ```likec4 include cloud.** // Same as include cloud.backend include cloud.ui include cloud.ui.webApp ``` #### Expand `._` The expand selector includes element's children **IF** they have a relationship with visible elements. All other children are omitted. ```likec4 include cloud._ // Same as include cloud include -> cloud.* -> ``` ## Relationship predicates Relationship predicates include elements only if they have relationships that meet the specified predicate conditions. ### Directed relationships Include elements if they have **directed** relationships (or their nested elements): ```likec4 // Include customer and cloud: include customer -> cloud // Include customer and nested elements of cloud (that have relationships): include customer -> cloud.* ``` ### Any relationship Include elements if they have any relationships: ```likec4 include customer <-> cloud ``` ### Incoming Include elements if they have incoming relationships from already visible elements. Here’s an example based on the model from the [wildcard example](#wildcard): ```likec4 wrap title="incoming predicate.c4" view { // visible element include customer // include nothing, customer has no relation to backend include -> backend // add ui, // because customer has a relationship with nested ui.webApp include -> ui // add backend, because visible ui has a relationship to backend // derived from ui.webApp -> backend.api include -> backend } // This view includes customer and ui view { include customer, -> cloud.* } ``` :::tip Relationship predicates are useful for refining your diagrams, allowing you to narrow the scope and focus on specific parts of the system. ::: ### Outgoing Include elements if only they have outgoing relationships to already visible elements: ```likec4 include customer -> include cloud.* -> ``` ### In/Out Include nested elements of `cloud`, that have any relationships with visible elements: ```likec4 include -> cloud.* -> ``` ### Relationship customization Relationships can be customized inside view: ```likec4 include // Make lines red and solid cloud.* <-> amazon.* with { color red line solid }, // or only directed customer -> cloud.* with { // Override label title 'Customer uses cloud' navigateTo dynamicview1 }, ``` :::tip Sometimes, connections may have a title `[...]`. This indicates that the connection has been merged from multiple relationships with different titles, and it was impossible to derive a definitive one. You can change it: ```likec4 include customer -> cloud with { // Change [...] to title 'Customer uses cloud' }, ``` ::: :::caution It is possible to customize relationships with known endpoints only (i.e. [directed](#directed-relationships) or [any between](#any-relationship)) For example, `* -> *` can be customized (in other words, all relationships on the view), but outgoing `cloud.* ->` can not. ::: ### Relationship navigation To customize [navigation](/dsl/relationships/#navigate-to) from relationship: ```likec4 include webApp -> backend.api with { navigateTo dashboardRequestFlow } ``` ## Filter `where` operator narrows down results by applying additional conditions: ```likec4 // include only microservices from nested include cloud.* where kind is microservice // only microservices and not deprecated include cloud.* where kind == microservice and // possible to use 'is' or '==' tag != #deprecated // possible to use 'is not' or '!=' // Use logical operators include cloud.* where not (kind is microservice or kind is webapp) and tag is not #legacy and (tag is #v1 or tag is #v2) ```
**Relationship predicates** When `where` is used with element predicates, it is applied to the elements. When used with relationship predicates - to the relationships. ```likec4 include // only relationships with tag #messaging cloud.* <-> amazon.* where tag is #messaging, // only incoming http-requests -> backend where kind is http-request -[http-request]-> backend .http-request backend ``` It is also possible to filter relations by tag or kind of its endpoints. ```likec4 include // only relationships outgoing from elements with with tag #next cloud.* -> amazon.* where source.tag is #next, // only incoming relations of elements with kind microservice -> * where target.kind is microservice ```
**Together with `with`** It is possible to use `where` together with `with`, but `where` should be defined first: ```likec4 include * where kind is microservice with { color amber } ```
:::tip Less verbose and more satisfying results are achieved with `where` in `exclude` predicates. For example: ```likec4 // only keep elements tagged with #v1 exclude * where tag is not #v1 // only keep relationships tagged with #commands exclude * -> * where tag is not #commands ``` Together with [predicate groups](#global-predicate-groups) you may define a "baseline" (includes everything), and then filter out in inherited views. ::: ## Global predicate groups If you find yourself repeating the same predicates in multiple views, you can define them as global group: ```likec4 global { predicateGroup microservices { include cloud.* where kind is microservice exclude * where tag is #deprecated } } views { view of newServices { include cloud.new.* global predicate microservices } view of newBackendServices { // Keep in mind that order is significant global predicate microservices include cloud.backend.* } } ``` ## Groups It is possible to group elements, and this is rendered as a boundary around them: ```likec4 view { group { include backend } // with title group 'Frontend' { include frontend.* } // with style group 'Service Bus' { color amber opacity 20% border solid include messageBroker.* } } ``` Groups can be nested: ```likec4 view { group 'Third-parties' { group 'Integrations' { group 'Analytics' {} group 'Marketing' {} } group 'Monitoring' {} } } ``` :::note Order of predicates is significant.
How element predicates are grouped? For element predicates - element stays in first group it was included. ```likec4 group { include backend //wins group { include backend //ignored } } group { group { include api //wins } include api //ignored } ``` It is possible to change: ```likec4 group { include backend group { exclude backend include backend //wins } } ```
How relationship predicates are grouped? For relationship predicates - the last one "wins": ```likec4 group { include -> backend group { include -> backend //wins } } group { group { include -> backend } include -> backend //wins } ```
::: ## Style predicates Style predicates define how elements are rendered, and applied in the order they are defined merging with previous ones: ```likec4 view apiApp of internetBankingSystem.apiApplication { include * // apply to all elements style * { color muted opacity 10% } // apply only to these elements style singlePageApplication, mobileApp { color secondary size xlarge } // apply only to nested of apiApplication style apiApplication.* { color primary multiple true } // apply to apiApplication and nested style apiApplication._ { color primary } // apply only to elements with specific tag style element.tag = #deprecated { color muted } // apply to elements not tagged style element.tag != #deprecated { opacity 20% } } ``` :::caution [`Group`](#groups) does not support nested `style` predicates (yet). ::: ### Shared local styles Styles can be shared within `views` block (_"local styles"_): ```likec4 views { // apply to all views in this block style * { color muted opacity 10% } view of apiApp { include * style cloud.web.* { color green } } view of mobileApp { include * style cloud.ui.* { color amber } } } views { // Styles from previous block are not applied here // ... } ``` :::caution [Overrides](#with-overrides) are always applied last, after all styles ::: ### Shared global styles Styles can be shared globally. Global styles must be named and defined in `global` block: ```likec4 {4,9,15,25,33-34} global { // Format: // style { ... } style mute_all * { color muted opacity 10% } style applications singlePageApplication._, mobileApp._ { color secondary } style mute_deprecated element.tag = #deprecated { color muted } } views { view of singlePageApplication { // Styles are applied in the order they are defined // 1. Apply global style global style mute_all // 2. Then this style cloud.* { color green } // 3. and 4. global style applications global style mute_deprecated } } ``` ### Shared style groups Global styles can be grouped: ```likec4 {3,18} global { // Define style group styleGroup common_styles { style singlePageApplication, mobileApp { color secondary } style element.tag = #deprecated { color muted } } } views { view mobileApp of mobileApp { include * // Apply styles from group global style common_styles // Override style mobileApp { color primary } } } ``` :::tip Global styles and groups can be used as `views`-locals: ```likec4 global { style mute_all * { color muted } styleGroup theme1 { //... styleGroup theme2 { //... } // All views within this block have styles from 'theme1' views { global style theme1 view view1 { //... } // All views have 'mute_all' style and all from 'theme2' views { global style mute_all global style theme2 view view2 { //... } ``` ::: ## Auto-layout ```likec4 view { include * autoLayout LeftRight 120 110 } ``` Parameters are: - direction: possible values are `TopBottom` (default), `BottomTop`, `LeftRight`, `RightLeft`. - rank distance: optional, must be a positive number - node distance. optional, must be a positive number ## Extend views Views can be extended to avoid duplication, to create a "baseline" or, for example, "slides" for a presentation: ```likec4 views { view view1 { include * } view view2 extends view1 { title 'Same as View1, but with more details' style * { color muted } include some.backend } // cascade inheritance view view3 extends view2 { title 'Same as View2, but with more details' include * -> some.backend } } ``` The predicates and style rules of extended views applied after the ones from ancestors. Extended view also inherits the scope: ```likec4 views { view view1 of cloud.backend { title 'Backend components' } view view2 extends view1 { include api // ✅ This is OK, references 'cloud.backend.api' } } ``` # Custom Generators import { PackageManagers } from 'starlight-package-managers' Ensure you have [`likec4`](https://www.npmjs.com/package/likec4) in your dependencies:
You can define custom generators in your project config: ```ts // likec4.config.ts import { defineConfig } from 'likec4/config' export default defineConfig({ name: 'my-project', title: 'My Project', generators: { 'hello': async ({ likec4model, ctx }) => { await ctx.write({ path: 'hello.txt', // relative to the project root content: `Project: ${likec4model.project.id}`, }) }, }, }) ``` Now you can run your generator with [CLI](/tooling/cli): ```bash likec4 gen hello ``` In [multi-project](/dsl/config/multi-projects) workspace use: ```bash likec4 gen hello --project my-project # Other options likec4 gen hello --project my-project --use-dot ``` ## Reusable Generators There is also helper function `defineGenerators` to define reusable generators: ```ts // shared_generators.ts import { defineGenerators } from 'likec4/config' export default defineGenerators({ 'hello': async ({ likec4model, ctx }) => { await ctx.write({ path: 'hello.txt', // relative to the project root content: `Project: ${likec4model.project.id}`, }) }, }) ``` Now you can use it in your configs: ```ts // likec4.config.ts import { defineConfig } from 'likec4/config' import generators from './shared_generators' export default defineConfig({ name: 'my-project', title: 'My Project', generators, }) ``` :::tip You can also use smaller package `@likec4/config`: ```ts import { defineGenerators } from '@likec4/config' export default defineGenerators({ 'hello': async ({ likec4model, ctx }) => { await ctx.write({ path: 'hello.txt', // relative to the project root content: `Project: ${likec4model.project.id}`, }) }, }) ``` ::: # Generate LikeC4Model import { PackageManagers } from 'starlight-package-managers';

![NPM Version](https://img.shields.io/npm/v/likec4) ![NPM Downloads](https://img.shields.io/npm/dm/likec4)

Generate source code artifacts from architecture model. ## Typed Model # Generate React import { PackageManagers } from 'starlight-package-managers'

![NPM Version](https://img.shields.io/npm/v/likec4) ![NPM Downloads](https://img.shields.io/npm/dm/likec4)

Generate React components with views from your architecture model. ## Install Ensure you have [`likec4`](https://www.npmjs.com/package/likec4) in your dependencies:
## React The following command generates a JavaScript bundle with React Component (and `.d.ts`):
```sh frame="none" # Aliases npx likec4 generate react -o ./src/likec4.generated.js npx likec4 gen react -o ./src/likec4.generated.js ``` :::note Check `likec4 codegen react --help` for available options. ::: To use the component: ```tsx import { LikeC4View } from './likec4.generated' const App = () => { return (
) } ``` | Property | Description | | ----------------- | --------------------------------------------------------------------------------------------------- | | `viewId` | Typed enumeration of your views | | `where` | Optional, see [filter](#filter) | | `injectFontCss` | Injects CSS with IBM Plex Sans font from CDN.
Default is `true` | :::tip Check source code for all properties. ::: :::caution `LikeC4View` does not rehydrate correctly if rendered on the server, prefer client-side. ::: ### Filter `where` is same [view predicate](/dsl/views/predicates/#filter), but applies dynamically and enables to show/hide elements based on the context. For example: ```tsx import { LikeC4View } from './likec4.generated' // Keeps elements and relationships where: // - tag is not 'legacy' // - and // - tag is 'v1' or 'v2' const App = () => { return (
) } ``` Layout stays the same, i.e. elements are not rearranged. Be aware, `where` applies both to elements and relationships. ## ReactLikeC4 `LikeC4View` renders views from your model, and allows exploring in the popup browser. Component works in most usecases, but if you need more - use `ReactLikeC4`: ```tsx import { ReactLikeC4, type LikeC4ViewId } from './likec4.generated' const App = () => { const [viewId, setViewId] = useState('index') return ( ) } ``` `ReactLikeC4` is a low-level component, giving you more control and allowing react to the events. Check source code for available options. Feel free to share your ideas or ask questions in GitHub discussions. ## Styling # Generate Web Components import { PackageManagers } from 'starlight-package-managers'

![NPM Version](https://img.shields.io/npm/v/likec4) ![NPM Downloads](https://img.shields.io/npm/dm/likec4)

## Install Ensure you have [`likec4`](https://www.npmjs.com/package/likec4) in your dependencies:
## Web Component Generate javascript bundle with web component:
Use it: ```html ``` By default, cli generates a `likec4-view` web component. You can change the `likec4` prefix by `-w, --webcomponent-prefix`. For example: And in HTML: ```html ``` | Property | Description | | ----------------- | ----------------------------------------------------------------------------------------------------- | | `view-id` | Your view id | | `browser` | Whether to show views browser popup (default `true`) | | `dynamic-variant` | How dynamic view should be rendered
Possible values: `diagram` or `sequence` (default `diagram`) | :::note CLI command [build](/tooling/cli/#build-static-website) always generates javascript with web components. Check `Share` button on the top at this example :::