Engineering Journal
Schema Editor
Schema Editor

One Codebase, N Deployment Contexts: The Web Component Configuration Pattern

2026-06-04

TLDR

Deploying specialized variants of a canvas tool (e.g., Electrical Schematics, AEC Floorplans, UML Diagrams) by copying and pasting main HTML/JS files leads to code drift and fragmented bug fixes. Encapsulating the core canvas engine inside a Web Component (<schema-editor>) and passing domain configurations via HTML attributes (domain="electrical") allows host pages to act as thin shells while maintaining a single, unified engine codebase.
Deployment ModelSource Code StructureBug Fix MaintenanceScalability for New Domains
Copy-Pasted HTML FilesN independent copiesHigh (Fixes must be manually copied)Fragile (Code drift within weeks)
Web Component ShellsSingle Canonical Engine (schema-editor.js)Zero (Fixes apply everywhere)High (New domain = 15-line shell)

Problem statement: the multi-domain drift tax

Our schema editor supports multiple specialized engineering domains:

Initially, each domain was created by copying index.html into domain subdirectories (electrical/index.html, floorplan/index.html).

Within weeks, bug fixes applied to electrical/index.html were omitted from floorplan/index.html, causing feature drift and duplicate bug reports.


Technical failure mode: fragmented code engines

Duplicating HTML and script setups produces independent codebase copies:

  1. Divergent Implementations: Engine improvements made in one file are missed in others.
  2. High Maintenance Overhead: Updating canvas routing or rendering logic requires editing N separate files.

The fix & architecture: Web component encapsulation

Encapsulate core canvas logic inside a Web Component custom element (<schema-editor>). Domain pages import the component script and pass configuration via the domain attribute:

Step 1: Web component definition (schema-editor.js))

// Central Canvas Engine Web Component
class SchemaEditorElement extends HTMLElement {
  static get observedAttributes() {
    return ['domain'];
  }

connectedCallback() { const domain = this.getAttribute('domain') || 'general'; this.initEngine(domain); }

attributeChangedCallback(name, oldValue, newValue) { if (name === 'domain' && oldValue !== newValue) { this.switchDomainConfig(newValue); } }

initEngine(domainKey) { const config = DOMAIN_CONFIGURATIONS[domainKey] || DOMAIN_CONFIGURATIONS.general;

this.setupPalette(config.palette); this.setupGridSnap(config.snapGridSize); this.setupExporters(config.exportFormats); } }

customElements.define('schema-editor', SchemaEditorElement);

Step 2: centralized configuration registry

const DOMAIN_CONFIGURATIONS = {
  electrical: {
    palette: ['resistor', 'capacitor', 'ic', 'gnd'],
    snapGridSize: 10,
    exportFormats: ['svg', 'netlist', 'bom']
  },
  floorplan: {
    palette: ['wall', 'door', 'window', 'hvac'],
    snapGridSize: 50,
    exportFormats: ['svg', 'dxf']
  }
};

Step 3: declarative host page shells

Domain host pages become 15-line shells:
<!-- electrical/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Electrical Schematic Editor</title>
  <script type="module" src="/src/components/schema-editor.js"></script>
</head>
<body>
  <schema-editor domain="electrical"></schema-editor>
</body>
</html>
Rule of thumb: Encapsulate multi-context tools inside Web Components using attribute interfaces (domain="electrical"). Treat domain HTML files as thin declarative shells.
Read this post in the full Engineering Journal →