JavaScript Architecture¶
Overview¶
Wilco’s JavaScript layer enables Python backends to serve React components without requiring a full frontend build pipeline. The architecture consists of three main parts:
Standalone Loader (
loader.js) - Self-contained runtime that renders components on any HTML pageLive Loader (
live-loader.js) - Extension for Django admin live previewComponent Bundles - ESM bundles created by esbuild at runtime
All JavaScript assets are pre-built and included in the Python wheel, so users don’t need Node.js installed to use wilco. However, esbuild is required for runtime component bundling.
File structure¶
src/
├── wilco/bridges/django/static/wilco/
│ ├── loader.js # Pre-built standalone loader (~195KB)
│ └── live-loader.js # Live preview extension (~9KB)
└── wilcojs/react/
└── src/loader/
└── standalone.ts # TypeScript source for loader.js
The loader.js file is compiled from standalone.ts using esbuild. This
happens during package release (via make wheel), not at install time.
Standalone loader¶
The standalone loader (loader.js) is a self-contained IIFE that bundles:
React 19 and ReactDOM - Full React runtime
goober - Lightweight CSS-in-JS library (~1KB)
Error Boundary - Catches and displays component errors
Source Map Support - Maps errors to original TypeScript
Size: ~195KB minified (includes React)
How it works¶
Waits for
DOMContentLoadedFinds elements with
data-wilco-componentattributeFetches component bundle from
/api/bundles/{name}.jsTransforms ESM imports to use the module registry
Executes the bundle and renders the component
Example usage:
<div
data-wilco-component="store:product"
data-wilco-props='{"name": "Widget", "price": 9.99}'
data-wilco-api="/api"
data-wilco-hash="abc123">
Loading...
</div>
<script src="/static/wilco/loader.js" defer></script>
Module registry¶
Components are bundled with external dependencies (React, goober) that are
provided at runtime via window.__MODULES__:
window.__MODULES__ = {
"react": React,
"react/jsx-runtime": ReactJsxRuntime,
"@wilcojs/react": { useComponent },
"goober": goober,
};
This allows components to use standard imports:
import React, { useState } from "react";
import { styled } from "goober";
useComponent hook¶
The loader provides a useComponent hook for dynamic component loading
with React Suspense:
import { useComponent } from "@wilcojs/react";
function ProductList({ products }) {
const ProductCard = useComponent("store:product");
return products.map(p => <ProductCard {...p} />);
}
The hook integrates with React Suspense, so components automatically show a loading fallback while child components load.
Static mode¶
When pre-built bundles are available, the loader can serve bundles from static
file URLs instead of the API. This is controlled via two global variables set
on the window object:
window.staticManifest = {
"store:product": {
"file": "bundles/store--product.a1b2c3d4.js",
"hash": "a1b2c3d4"
}
};
window.staticManifestBaseUrl = "/static/wilco/";
When staticManifest is set, the loader:
Looks up the component in the manifest
Loads the bundle from
{staticManifestBaseUrl}{file}instead of the APIFalls back to API loading if the component is not in the manifest
The Django template tag {% wilco_loader_script %} automatically sets these
variables when WILCO_BUILD_DIR is configured and contains a valid manifest.
These variables are stored on window (not as module-level variables) to
survive duplicate <script> includes of loader.js.
Global API¶
The loader exposes window.wilco for programmatic control:
// Load a component
const Component = await window.wilco.loadComponent("store:product");
// Render into a container
await window.wilco.renderComponent(container, "store:product", props);
// Update props on a mounted component
window.wilco.updateComponentProps(container, newProps);
Live loader¶
The live loader (live-loader.js) extends the standalone loader for Django
admin integration. It provides live preview that updates as you edit forms.
Features:
Debounced Updates - Waits 300ms after field changes
Server Validation - POSTs form data to validate endpoint
Error Display - Shows validation errors above preview
Blob URL Preview - Shows uploaded images before save
Usage:
<div
data-wilco-component="store:product_preview"
data-wilco-props='{"name": "Widget"}'
data-wilco-live="true"
data-wilco-validate-url="/admin/store/product/123/validate_preview/">
</div>
<script src="/static/wilco/loader.js" defer></script>
<script src="/static/wilco/live-loader.js" defer></script>
Image preview¶
When users select an image file, the live loader creates a blob URL for instant preview without uploading:
// Field "image" gets mapped to prop "imageUrl"
const blobUrl = URL.createObjectURL(file);
props.imageUrl = blobUrl;
This works transparently with components that accept imageUrl props.
Building the loader¶
The loader is pre-built in the wheel, but for development:
# Build loader only
make build-loader
# Build wheel (includes loader build)
make wheel
Or directly:
cd src/wilcojs/react
pnpm build:loader
The build command uses esbuild:
esbuild src/loader/standalone.ts \
--bundle \
--minify \
--format=iife \
--outfile=../../wilco/bridges/django/static/wilco/loader.js
Runtime requirements¶
To use wilco components (render on pages):
No Node.js required
JavaScript files included in wheel
Modern browser with ES2020 support
To create components (bundle TypeScript at runtime):
esbuild must be available in PATH
Or: Node.js with
npx(downloads esbuild on demand)
Install esbuild globally:
npm install -g esbuild
Or let wilco use npx to download it automatically.
Component bundles¶
When a component is requested, wilco’s Python bundler:
Locates the component’s
index.tsxfileRuns esbuild with external dependencies
Returns ESM bundle with inline source maps
Bundler configuration:
bundle_component(
ts_path=Path("components/product/index.tsx"),
component_name="store:product",
external_deps=["react", "react-dom", "react/jsx-runtime",
"@wilcojs/react", "goober"]
)
External dependencies are resolved via the module registry at runtime.
See also¶
Standalone Loader - Detailed loader internals
CLI Reference - CLI reference for
wilco buildDjango Integration - Django integration guide
FastAPI Integration - FastAPI integration guide