Draw.io ER Diagram Implementation
Purpose
This skill enables correct generation of entity-relationship diagrams, database schemas, and data models in Draw.io mxGraph XML. It enforces crow's foot notation for cardinality, proper entity table structures, and PK/FK visual conventions. The most common AI mistake is using generic arrows (endArrow=classic) instead of ER-specific markers (ERone, ERmany, etc.).
Critical Rules (Memorize These)
- ALWAYS use
entityRelationEdgeStylefor ER relationships — NEVER useorthogonalEdgeStyleor default edge routing. - ALWAYS use ER-specific arrow markers (
ERone,ERmany,ERmandOne,ERzeroToOne,ERoneToMany,ERzeroToMany) — NEVER useendArrow=classicorendArrow=blockin ER diagrams. - ALWAYS set
startFill=0andendFill=0on ER edges — crow's foot markers MUST useFill=0. SettingFill=1produces incorrect filled shapes. - ALWAYS use
shape=table;childLayout=tableLayout;container=1;for entity shapes — this produces the standard database table appearance with a header row and stacked attribute rows. - ALWAYS place attribute rows as children of the entity — every attribute
mxCellMUST haveparent="<entity_id>". NEVER useparent="1". - ALWAYS mark primary key fields with
fontStyle=4;(underline) — this is the standard ER convention for PKs. - ALWAYS mark foreign key fields with
fontStyle=2;(italic) — this distinguishes FKs from regular attributes. - ALWAYS include both structural cells (
id="0"andid="1"withparent="0") in every diagram.
Entity Table Shape
Container Style (Header Row)
shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;
Key properties:
shape=table— renders as a table container with header band.startSize=30— header height in pixels. Controls the title band.container=1— REQUIRED. Without it, child cells will not attach.childLayout=tableLayout— auto-stacks attribute rows vertically.collapsible=1— allows collapsing the entity in the editor.fontStyle=1— bold entity name in header.resizeLast=1— last row stretches to fill remaining space.
Attribute Row Style
text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;
Every attribute row:
- MUST be a child of the entity container (
parent="<entity_id>"). - MUST have
yoffset calculated as:startSize + (row_index * row_height). - MUST have the same
widthas the parent entity. - Uses a standard
heightof 30px per row.
PK/FK Font Conventions
| Field Type | fontStyle | Visual | Example Value |
|---|---|---|---|
| Primary Key | fontStyle=4; |
Underlined | customer_id PK INT |
| Foreign Key | fontStyle=2; |
Italic | customer_id FK INT |
| Regular field | (none) | Normal | name VARCHAR(100) |
| PK + FK (composite) | fontStyle=6; |
Underline + Italic | order_id PK FK INT |
Entity Dimensions
| Component | Width | Height | Notes |
|---|---|---|---|
| Entity container | 200 | startSize + (row_count * 30) | Minimum 200px wide |
| Header band | 200 | 30 (startSize) | Contains entity name |
| Attribute row | 200 | 30 | Each field gets one row |
Height Calculation
Total entity height = startSize + (number_of_attributes * 30).
Example: Entity with 4 attributes = 30 + (4 * 30) = 150px.
Crow's Foot Notation Reference
Cardinality Markers
| Marker | Arrow Value | Symbol | Meaning |
|---|---|---|---|
| One (mandatory) | ERone |
Single line | Exactly one |
| Many | ERmany |
Crow's foot (fork) | One or more |
| Mandatory One | ERmandOne |
Line + single line | Exactly one (mandatory) |
| Zero-or-One | ERzeroToOne |
Circle + single line | Zero or one (optional) |
| One-or-Many | ERoneToMany |
Line + crow's foot | At least one, possibly many |
| Zero-or-Many | ERzeroToMany |
Circle + crow's foot | Zero, one, or many |
Common Relationship Patterns
| Relationship | startArrow | endArrow | Description |
|---|---|---|---|
| One-to-One (mandatory) | ERone |
ERone |
Exactly one on each side |
| One-to-Many | ERone |
ERmany |
Source has one, target has many |
| One-to-Many (mandatory) | ERmandOne |
ERoneToMany |
Must have at least one on each side |
| One-to-Many (optional) | ERzeroToOne |
ERzeroToMany |
Zero or more on each side |
| Many-to-Many | ERmany |
ERmany |
Many on both sides |
| Zero-or-One to Many | ERzeroToOne |
ERmany |
Optional source, many targets |
Relationship Edge Style
Standard ER Edge
edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;endFill=0;startArrow=ERone;startFill=0;strokeColor=#666666;
Key properties:
edgeStyle=entityRelationEdgeStyle— REQUIRED. Produces the classic ER curved connector.startArrow=ERone/endArrow=ERmany— crow's foot markers at each end.startFill=0/endFill=0— REQUIRED. ER markers MUST be unfilled.strokeColor=#666666— neutral gray for relationship lines.
Relationship Labels
Add a value attribute to the edge to label the relationship:
<mxCell id="rel1" value="places" style="edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;endFill=0;startArrow=ERone;startFill=0;" edge="1" parent="1" source="ent_customer" target="ent_order">
<mxGeometry relative="1" as="geometry" />
</mxCell>
Entity Color Conventions
Use distinct colors per entity to visually separate tables:
| Entity Role | Fill Color | Stroke Color |
|---|---|---|
| Primary entity | #dae8fc (light blue) |
#6c8ebf |
| Secondary entity | #d5e8d4 (light green) |
#82b366 |
| Junction/bridge table | #fff2cc (light yellow) |
#d6b656 |
| Lookup/reference table | #e1d5e7 (light purple) |
#9673a6 |
| Audit/log table | #f5f5f5 (light gray) |
#666666 |
Layout Strategy
Entity Spacing
- Horizontal spacing: 120-180px between entities placed side by side.
- Vertical spacing: 80-120px between entities placed above/below.
- Relationship lines:
entityRelationEdgeStyleauto-routes curves; no manual waypoints needed.
Positioning Guidelines
- Place primary entities (Customer, Product) on the left or top.
- Place dependent entities (Order, OrderLine) to the right or below.
- Place junction tables between the entities they bridge.
- Align entity tops when placed in a row for visual consistency.
Complete Example: E-Commerce Database (3 Entities)
<mxGraphModel>
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<!-- Entity: Customer -->
<mxCell id="ent_cust" value="Customer" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="60" y="80" width="200" height="120" as="geometry" />
</mxCell>
<mxCell id="cust_pk" value="customer_id PK INT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;fontStyle=4;" vertex="1" parent="ent_cust">
<mxGeometry y="30" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="cust_f1" value="name VARCHAR(100)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="ent_cust">
<mxGeometry y="60" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="cust_f2" value="email VARCHAR(255)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="ent_cust">
<mxGeometry y="90" width="200" height="30" as="geometry" />
</mxCell>
<!-- Entity: Order -->
<mxCell id="ent_ord" value="Order" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="380" y="60" width="200" height="150" as="geometry" />
</mxCell>
<mxCell id="ord_pk" value="order_id PK INT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;fontStyle=4;" vertex="1" parent="ent_ord">
<mxGeometry y="30" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="ord_fk" value="customer_id FK INT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;fontStyle=2;" vertex="1" parent="ent_ord">
<mxGeometry y="60" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="ord_f1" value="order_date DATE" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="ent_ord">
<mxGeometry y="90" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="ord_f2" value="total DECIMAL(10,2)" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="ent_ord">
<mxGeometry y="120" width="200" height="30" as="geometry" />
</mxCell>
<!-- Entity: OrderLine -->
<mxCell id="ent_line" value="OrderLine" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1">
<mxGeometry x="700" y="60" width="200" height="150" as="geometry" />
</mxCell>
<mxCell id="line_pk" value="line_id PK INT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;fontStyle=4;" vertex="1" parent="ent_line">
<mxGeometry y="30" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="line_fk1" value="order_id FK INT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;fontStyle=2;" vertex="1" parent="ent_line">
<mxGeometry y="60" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="line_fk2" value="product_id FK INT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;fontStyle=2;" vertex="1" parent="ent_line">
<mxGeometry y="90" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="line_f1" value="quantity INT" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;whiteSpace=wrap;html=1;" vertex="1" parent="ent_line">
<mxGeometry y="120" width="200" height="30" as="geometry" />
</mxCell>
<!-- Relationship: Customer 1:N Order -->
<mxCell id="rel1" value="places" style="edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;endFill=0;startArrow=ERone;startFill=0;strokeColor=#666666;" edge="1" parent="1" source="ent_cust" target="ent_ord">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<!-- Relationship: Order 1:N OrderLine -->
<mxCell id="rel2" value="contains" style="edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;endFill=0;startArrow=ERone;startFill=0;strokeColor=#666666;" edge="1" parent="1" source="ent_ord" target="ent_line">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
This example demonstrates:
- 3 entities with proper table containers and stacked attribute rows.
- PK fields with
fontStyle=4(underline) and FK fields withfontStyle=2(italic). - Crow's foot notation (
ERonetoERmany) on both relationships. entityRelationEdgeStylefor ER-specific edge routing.- Distinct colors per entity role (primary blue, secondary green, junction yellow).
- Relationship labels (
places,contains) on edges.
Alternative: Swimlane Entity Pattern
An alternative entity representation uses swimlane with childLayout=stackLayout:
swimlane;fontStyle=1;align=center;startSize=26;container=1;collapsible=0;childLayout=stackLayout;fillColor=#dae8fc;strokeColor=#6c8ebf;
This pattern is used in UML class diagrams and works for ER entities. The shape=table;childLayout=tableLayout pattern is PREFERRED for ER diagrams because it provides better table-like rendering with row separators.
Checklist: Before Outputting an ER Diagram
- Every entity uses
shape=table;container=1;childLayout=tableLayout;. - Every attribute row is a child of its entity (
parent="<entity_id>"). - Every PK field has
fontStyle=4;(underline). - Every FK field has
fontStyle=2;(italic). - Every relationship edge uses
edgeStyle=entityRelationEdgeStyle;. - Every relationship edge uses
ER*arrow types — NEVERclassicorblock. - Every ER arrow has
startFill=0;endFill=0;. - Both structural cells (
id="0"andid="1") are present. - All edge
sourceandtargetattributes point to valid entity cell IDs. - Entity heights match:
startSize + (attribute_count * 30). - Attribute row widths match the parent entity width.
- Cell IDs are unique across the entire diagram.
Cross-References
- XML fundamentals: See
drawio-core-xml-formatfor file structure and structural cells. - Style syntax: See
drawio-syntax-stylesfor the complete style property reference. - Connection details: See
drawio-syntax-connectionsfor edge routing and waypoints. - Geometry: See
drawio-core-geometryfor coordinate system and positioning.
Reference Links
- Complete Reference
- Working Examples
- Anti-Patterns