Integrations
How Domma components work together - blueprint-centric architecture, data flow, and event coordination.
Overview
Domma uses a blueprint-centric architecture. Define your data structure once in a Blueprint, then use it across Forms, Models, Tables, and CRUD operations.
Blueprints
Schema definition
Models
Reactive data
Forms
Auto-generated
HTTP
API sync
Storage
Persistence
Tables
Data display
<div class="grid grid-cols-1 grid-cols-md-2 grid-cols-lg-3 gap-4 mb-4"><div class="card text-center">
<div class="card-body">
<span data-icon="file-code" data-size="32" class="text-primary mb-2"></span>
<h4 class="text-sm font-semibold">Blueprints</h4>
<p class="text-xs text-muted">Schema definition</p>
</div>
</div>
<div class="card text-center">
<div class="card-body">
<span data-icon="database" data-size="32" class="text-success mb-2"></span>
<h4 class="text-sm font-semibold">Models</h4>
<p class="text-xs text-muted">Reactive data</p>
</div>
</div>
<div class="card text-center">
<div class="card-body">
<span data-icon="layout" data-size="32" class="text-info mb-2"></span>
<h4 class="text-sm font-semibold">Forms</h4>
<p class="text-xs text-muted">Auto-generated</p>
</div>
</div>
<div class="card text-center">
<div class="card-body">
<span data-icon="globe" data-size="32" class="text-warning mb-2"></span>
<h4 class="text-sm font-semibold">HTTP</h4>
<p class="text-xs text-muted">API sync</p>
</div>
</div>
<div class="card text-center">
<div class="card-body">
<span data-icon="save" data-size="32" class="text-secondary mb-2"></span>
<h4 class="text-sm font-semibold">Storage</h4>
<p class="text-xs text-muted">Persistence</p>
</div>
</div>
<div class="card text-center">
<div class="card-body">
<span data-icon="grid" data-size="32" class="text-danger mb-2"></span>
<h4 class="text-sm font-semibold">Tables</h4>
<p class="text-xs text-muted">Data display</p>
</div>
</div>
</div>Data Flow Diagram
This diagram shows how data flows through Domma components:
<div class="flow-step">Blueprint</div>
<div class="flow-arrow">→</div>
<div class="flow-step">Forms / Models</div>
<div class="flow-arrow">→</div>
<div class="flow-step">Validation</div>
<br>
<div class="flow-arrow" style="transform: rotate(90deg); margin: 1rem 0;">↓</div>
<br>
<div class="flow-step">HTTP / Storage</div>
<div class="flow-arrow">→</div>
<div class="flow-step">Tables / DOM</div>Key Concept: The Blueprint is the single source of truth. All components derive their behavior from it.
The Blueprint-Centric Approach
Define once, use everywhere. Create a single Blueprint and use it to power Forms, Models, and CRUD operations automatically.
Single Blueprint Definition
const userBlueprint = {
name: {
type: 'string',
required: true,
minLength: 2,
label: 'Full Name'
},
email: {
type: 'email',
required: true,
label: 'Email Address'
},
age: {
type: 'number',
min: 18,
max: 125,
label: 'Age'
}
};
Use Everywhere
// Auto-generate a form
const form = F.create(userBlueprint);
// Create a reactive model
const model = M.create(userBlueprint);
// Full CRUD with one line
F.crud('#app', userBlueprint, {
api: '/api/users'
});
<button type="button" class="btn btn-primary" id="demo-blueprint-btn">Try It: See All Three in Action</button>
<div class="result-pane mt-3" id="blueprint-demo-result" style="display: none;">
<div class="flex flex-wrap items-center gap-3 mb-3">
<button type="button" class="btn btn-sm btn-secondary" data-view="form">Form View</button>
<button type="button" class="btn btn-sm btn-secondary" data-view="model">Model View</button>
<button type="button" class="btn btn-sm btn-secondary" data-view="table">Table View</button>
</div>
<div id="blueprint-demo-content"></div>
</div>/**
* Use Everywhere - from public/showcase/integrations.
*/
export function mount(root) {
// Blueprint Power Demo
const blueprintDemoBtn = $(root).find('#demo-blueprint-btn');
const blueprintResult = $(root).find('#blueprint-demo-result');
const blueprintContent = $(root).find('#blueprint-demo-content');
let currentView = 'form';
// Shared data storage for the demo
let demoData = [
{ name: 'John Doe', email: 'john@example.com', age: 30 },
{ name: 'Jane Smith', email: 'jane@example.com', age: 28 }
];
let demoModel = null;
let demoTable = null;
const demoBlueprint = {
name: { type: 'string', required: true, label: 'Name', placeholder: 'Enter name' },
email: { type: 'email', required: true, label: 'Email', placeholder: 'you@example.com' },
age: { type: 'number', min: 18, max: 125, label: 'Age' }
};
blueprintDemoBtn.on('click', () => {
blueprintResult.show();
renderBlueprintView('form');
});
function renderBlueprintView(view) {
currentView = view;
// Update button states
$('[data-view]').removeClass('btn-primary').addClass('btn-secondary');
$(`[data-view="${view}"]`).removeClass('btn-secondary').addClass('btn-primary');
if (view === 'form') {
// Build HTML with container first
const html = `
<h4 class="font-semibold mb-3">Generated Form</h4>
<p class="text-muted mb-3">Fill out the form and submit to add data to the table →</p>
<div id="demo-form-container"></div>
`;
blueprintContent.html(html);
// Small delay to ensure DOM is ready
setTimeout(() => {
F.render('#demo-form-container', demoBlueprint, {}, {
layout: 'stacked',
submitText: 'Add to Table',
onSubmit: (data) => {
// Add to shared data array
demoData.push(data);
// Update model if it exists
if (demoModel) {
demoModel.set(data);
}
// Show success and switch to table view
E.toast(`Added ${data.name} to table!`, { type: 'success' });
console.log('Form data:', data);
console.log('All data:', demoData);
// Auto-switch to table view after 500ms
setTimeout(() => {
renderBlueprintView('table');
}, 500);
}
});
}, 10);
} else if (view === 'model') {
// Use latest data from array, or create default
const latestData = demoData.length > 0
? demoData[demoData.length - 1]
: { name: 'John Doe', email: 'john@example.com', age: 30 };
// Create or update model
if (!demoModel) {
demoModel = M.create(demoBlueprint, latestData);
} else {
demoModel.set(latestData);
}
const html = `
<h4 class="font-semibold mb-3">Model State (JSON)</h4>
<p class="text-muted mb-3">This model reflects the latest form submission</p>
<pre style="background: #282c34; color: #abb2bf; padding: 1rem; border-radius: 4px; overflow-x: auto; font-family: 'Courier New', monospace; font-size: 13px;">${JSON.stringify(demoModel.toJSON(), null, 2)}</pre>
<p class="mt-3"><strong>Try it:</strong> Open browser console and type:</p>
<pre class="code-block language-javascript">
// Change model property
window.demoModel.set('name', 'Jane Smith');
// Get a value
window.demoModel.get('name');
// Validate the model
window.demoModel.validate();
// Get all data as JSON
window.demoModel.toJSON();
</pre>
`;
blueprintContent.html(html);
window.demoModel = demoModel;
} else if (view === 'table') {
// Build HTML with container
const html = `
<h4 class="font-semibold mb-3">Generated Table</h4>
<p class="text-muted mb-3">Showing ${demoData.length} record${demoData.length !== 1 ? 's' : ''} - Add more via the Form view</p>
<div id="demo-table-container"></div>
`;
blueprintContent.html(html);
// Small delay to ensure DOM is ready
setTimeout(() => {
// Create table with all data
demoTable = Domma.tables.create('#demo-table-container', {
columns: [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'age', label: 'Age' }
],
data: demoData,
striped: true,
hover: true,
pagination: demoData.length > 5 ? true : false,
itemsPerPage: 5
});
}, 10);
}
}
$('[data-view]').on('click', function() {
renderBlueprintView($(this).attr('data-view'));
});
}Data Flow Patterns
Common integration patterns showing how data moves between components.
Two-Way Binding
Forms can be automatically bound to Models for reactive updates:
const userModel = M.create(userBlueprint, {
name: 'John Doe',
email: 'john@example.com'
});
// Create form with model binding
const form = F.create(userBlueprint, {}, {
model: userModel // Two-way binding
});
// Changes in form update model automatically
// Changes in model update form automatically
<div id="data-flow-tabs">
<div class="tabs">
<div class="tab-list" role="tablist">
<button type="button" class="tab-item active" role="tab">Form → Model</button>
<button type="button" class="tab-item" role="tab">Model → DOM</button>
<button type="button" class="tab-item" role="tab">Model → HTTP</button>
<button type="button" class="tab-item" role="tab">Model → Storage</button>
</div>
<div class="tab-content">
<div class="tab-panel active">
<h4 class="mb-3">Two-Way Binding</h4>
<p>Forms can be automatically bound to Models for reactive updates:</p>
<pre class="code-block language-javascript">
const userModel = M.create(userBlueprint, {
name: 'John Doe',
email: 'john@example.com'
});
// Create form with model binding
const form = F.create(userBlueprint, {}, {
model: userModel // Two-way binding
});
// Changes in form update model automatically
// Changes in model update form automatically
</pre>
</div>
<div class="tab-panel" style="display:none;">
<h4 class="mb-3">Reactive DOM Binding</h4>
<p>Bind model properties directly to DOM elements:</p>
<pre class="code-block language-javascript">
const userModel = M.create(userBlueprint);
// Bind model fields to DOM elements
M.bind(userModel, 'name', '#display-name', {
twoWay: true // Updates both ways
});
M.bind(userModel, 'email', '#display-email');
// When model changes, DOM updates automatically
userModel.set('name', 'Jane Smith');
</pre>
</div>
<div class="tab-panel" style="display:none;">
<h4 class="mb-3">API Synchronization</h4>
<p>Sync models with REST APIs manually or use CRUD helper:</p>
<pre class="code-block language-javascript">
// Manual sync
const user = M.create(userBlueprint);
// Load from API
const data = await H.get('/api/users/1');
user.set(data);
// Save to API
const result = await H.put('/api/users/1',
user.toJSON()
);
// Or use CRUD helper for automatic sync
F.crud('#users', userBlueprint, {
api: '/api/users' // Handles all CRUD
});
</pre>
</div>
<div class="tab-panel" style="display:none;">
<h4 class="mb-3">localStorage Persistence</h4>
<p>Models can automatically persist to localStorage:</p>
<pre class="code-block language-javascript">
// Auto-persist model
const settings = M.create(settingsBlueprint, {}, {
persist: 'user-settings', // localStorage key
autoSave: true // Save on every change
});
// Manual save/load
settings.save();
settings.load();
// Check if persisted
if (settings.isPersisted()) {
console.log('Settings saved');
}
</pre>
</div>
</div>
</div>
</div>/**
* Data Flow Patterns - from public/showcase/integrations.
*
* The tab markup is static, so it lives in demo.html; E.tabs() only wires it up.
*/
export function mount(root) {
const tabs = E.tabs($(root).find('#data-flow-tabs .tabs')[0]);
return () => tabs?.destroy?.();
}Event-Driven Communication
Use Pub/Sub for decoupled component coordination. Components can communicate without direct references.
Publisher
// Component A publishes event
M.publish('cart:updated', {
items: cartItems,
total: calculateTotal()
});
Subscribers
// Component B listens
M.subscribe('cart:updated', (data) => {
updateBadge(data.items.length);
});
// Component C also listens
M.subscribe('cart:updated', (data) => {
showNotification(`Cart updated`);
});
<div class="flex flex-wrap items-center gap-3 mb-3">
<button type="button" class="btn btn-primary" id="add-to-cart-btn">Add to Cart</button>
<span class="badge badge-danger" id="cart-badge">0</span>
<span class="text-muted">← Watch the badge update via Pub/Sub</span>
</div>
<div id="event-notifications"></div>/**
* Subscribers - from public/showcase/integrations.
*/
export function mount(root) {
// Event Bridge Demo
let cartCount = 0;
const cartBadge = $(root).find('#cart-badge');
const addToCartBtn = $(root).find('#add-to-cart-btn');
const notificationsContainer = $(root).find('#event-notifications');
const unsubscribe = M.subscribe('cart:updated', (data) => {
cartBadge.text(data.count);
// Show notification
const notification = $('<div class="alert alert-success mb-2"></div>')
.text(`Item added! Cart has ${data.count} item${data.count !== 1 ? 's' : ''}.`)
.hide()
.appendTo(notificationsContainer)
.fadeIn();
setTimeout(() => {
notification.fadeOut(() => notification.remove());
}, 3000);
});
addToCartBtn.on('click', () => {
cartCount++;
M.publish('cart:updated', { count: cartCount });
});
return unsubscribe;
}CRUD in 3 Lines
The F.crud() method combines Forms, Models, Tables, and Validation into a complete CRUD interface with just one function call.
F.crud('#users', userBlueprint, {
api: '/api/users', // REST API endpoint
features: ['add', 'edit', 'delete', 'search']
});
This single line creates:
- ✓ Sortable, filterable data table
- ✓ Modal form for creating new records
- ✓ Edit functionality with pre-populated data
- ✓ Delete confirmation dialogs
- ✓ Built-in validation from the blueprint
- ✓ API or localStorage persistence
Pro Tip: Use
storage: 'storage-key'instead ofapifor localStorage-backed CRUD with zero backend code.
Styling Integration
Domma uses a modular CSS architecture with 4 files loaded in order:
<!-- 1. Base styles + utilities -->
<link rel="stylesheet" href="dist/domma.css">
<!-- 2. Grid system (Bootstrap + CSS Grid) -->
<link rel="stylesheet" href="dist/grid.css">
<!-- 3. UI components (22 elements) -->
<link rel="stylesheet" href="dist/elements.css">
<!-- 4. Theme system (10+ themes) -->
<link rel="stylesheet" href="dist/themes/domma-themes.css">
Theme Integration
All Domma components are theme-aware. Change the theme and everything updates together:
Card Component
Updates with theme
<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="">Light</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="dark" data-variant="">Dark</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="ocean">Ocean</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="forest">Forest</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="sunset">Sunset</button>
<button type="button" class="btn btn-sm btn-secondary" data-theme="reset">Reset</button>
<div class="grid grid-cols-1 grid-cols-md-3 gap-4">
<div class="card">
<div class="card-body">
<h5>Card Component</h5>
<p class="text-muted">Updates with theme</p>
</div>
</div>
<div class="card">
<div class="card-body">
<button type="button" class="btn btn-primary w-100">Button</button>
<button type="button" class="btn btn-secondary w-100 mt-2">Secondary</button>
</div>
</div>
<div class="card">
<div class="card-body">
<input type="text" class="form-input" placeholder="Form input">
<select class="form-select mt-2">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
</div>/**
* Theme Integration - from public/showcase/integrations.
*/
export function mount(root) {
// Theme Demo
$('[data-theme]').on('click', function() {
const theme = $(this).attr('data-theme');
const variant = $(this).attr('data-variant');
if (theme === 'reset') {
Domma.theme.set('ocean-light');
E.toast('Theme reset to Light', { type: 'success' });
} else {
Domma.theme.set(`${variant || 'ocean'}-${theme}`);
const themeName = variant ? _.capitalize(variant) : _.capitalize(theme);
E.toast(`Theme changed to ${themeName}`, { type: 'success' });
}
});
}Grid System
Use Bootstrap-style grid or CSS Grid utilities:
<!-- Bootstrap-style -->
<div class="row">
<div class="col-md-6">Column 1</div>
<div class="col-md-6">Column 2</div>
</div>
<!-- CSS Grid utilities -->
<div class="grid grid-cols-2 gap-4">
<div>Column 1</div>
<div>Column 2</div>
</div>
Quick Integration Reference
M.extend()
M.pick()
M.omit()
M.types.string
M.types.number
M.types.email
See Blueprints Showcase for complete reference.
<div id="reference-tabs">
<div class="tabs">
<div class="tab-list" role="tablist">
<button type="button" class="tab-item active" role="tab">Blueprint Methods</button>
<button type="button" class="tab-item" role="tab">Model Methods</button>
<button type="button" class="tab-item" role="tab">Form Methods</button>
<button type="button" class="tab-item" role="tab">Event Methods</button>
</div>
<div class="tab-content">
<div class="tab-panel active">
<div class="grid grid-cols-2 grid-cols-md-3 gap-3">
<code class="badge badge-primary">M.extend()</code>
<code class="badge badge-primary">M.pick()</code>
<code class="badge badge-primary">M.omit()</code>
<code class="badge badge-primary">M.types.string</code>
<code class="badge badge-primary">M.types.number</code>
<code class="badge badge-primary">M.types.email</code>
</div>
<p class="mt-3">See <a href="/showcase/blueprints">Blueprints Showcase</a> for complete reference.</p>
</div>
<div class="tab-panel" style="display:none;">
<div class="grid grid-cols-2 grid-cols-md-3 gap-3">
<code class="badge badge-success">M.create()</code>
<code class="badge badge-success">model.get()</code>
<code class="badge badge-success">model.set()</code>
<code class="badge badge-success">model.validate()</code>
<code class="badge badge-success">M.bind()</code>
<code class="badge badge-success">model.save()</code>
<code class="badge badge-success">model.load()</code>
<code class="badge badge-success">model.toJSON()</code>
<code class="badge badge-success">model.reset()</code>
</div>
<p class="mt-3">See <a href="/showcase/models">Models Showcase</a> for complete reference.</p>
</div>
<div class="tab-panel" style="display:none;">
<div class="grid grid-cols-2 grid-cols-md-3 gap-3">
<code class="badge badge-info">F.create()</code>
<code class="badge badge-info">F.crud()</code>
<code class="badge badge-info">F.modal()</code>
<code class="badge badge-info">F.wizard()</code>
<code class="badge badge-info">form.getData()</code>
<code class="badge badge-info">form.setData()</code>
<code class="badge badge-info">form.validate()</code>
<code class="badge badge-info">form.reset()</code>
</div>
<p class="mt-3">See <a href="/showcase/forms">Forms Showcase</a> for complete reference.</p>
</div>
<div class="tab-panel" style="display:none;">
<div class="grid grid-cols-2 grid-cols-md-3 gap-3">
<code class="badge badge-warning">M.subscribe()</code>
<code class="badge badge-warning">M.publish()</code>
<code class="badge badge-warning">M.unsubscribe()</code>
<code class="badge badge-warning">M.once()</code>
<code class="badge badge-warning">model.onChange()</code>
</div>
<p class="mt-3">See <a href="/showcase/models">Models Showcase</a> for Pub/Sub examples.</p>
</div>
</div>
</div>
</div>/**
* Quick Integration Reference - from public/showcase/integrations.
*
* The tab markup is static, so it lives in demo.html; E.tabs() only wires it up.
*/
export function mount(root) {
const tabs = E.tabs($(root).find('#reference-tabs .tabs')[0]);
return () => tabs?.destroy?.();
}