Workflow for Importing an Existing Static Site into Instatic: Complete Guide
Instatic treats static-site imports as first-class operations that transform HTML, CSS, and assets into editable page-tree nodes, design tokens, and media records through an atomic, reversible pipeline.
The CoreBunch/Instatic repository provides a comprehensive workflow for importing an existing static site that preserves your content structure while converting it into Instatic's native data models. This process runs entirely inside the single Bun server and utilizes the Super Import engine to parse source files, extract design tokens, and commit changes transactionally with full undo support.
Initiating the Import from the Admin UI
You can trigger an import through two entry points in the administrative interface. Access the command palette with ⌘K and select the import option, or click the Import button located in the Data workspace.
The interface supports two input methods:
- Drag-and-drop: Drop an entire folder containing your pre-built static site
- Direct paste: Paste raw HTML markup directly into the input field
Once files are submitted, the ImportPanel component located in src/admin/pages/site/ui/ImportPanel.tsx handles the file collection and forwards the data to the import engine.
The Super Import Pipeline: From Assets to Editable Nodes
The Super Import engine in src/core/import/superImport.ts processes dropped assets through a strict parsing pipeline. This engine converts static files into Instatic's internal representations while maintaining relationships between pages, styles, and media.
Parsing HTML into the NodeTree Structure
For every .html file discovered in the uploaded folder, the engine calls parseHtmlDocument and createNodeTreeFromHtml from @core/page-tree to generate a NodeTree structure. This transformation turns static markup into editable page-tree nodes that appear in the Pages panel.
Extracting Design Tokens and Styles
The importer analyzes CSS through multiple channels:
- Inline styles are extracted into
inlineStylesrecords - Linked stylesheets are parsed into structured
StyleRuleobjects - Color values are discovered and converted into Core Framework design tokens (
@core/framework)
These tokens populate the Design Tokens pane and remain linked to their originating elements.
Registering Media Assets
Images, fonts, and other static assets are processed by the media storage registry (@core/plugins/mediaStorageRegistry). Files matching patterns like /\.(png|jpe?g|svg|woff2?)$/ are registered via registerMedia and become addressable through the Media workspace.
Conflict Detection and Merge Resolution
Before writing any data, the importer executes a diff against the existing site using logic defined in server/writePolicy/siteDiff.ts. The system compares incoming page paths, token names, and media identifiers against current records.
If conflicts are detected, the UI presents a resolution dialog offering three options:
- Overwrite: Replace the existing item with the imported version
- Keep both: Rename the incoming item and preserve both versions
- Skip: Discard the conflicting item from the import
This validation layer uses the same type-safe checks (@core/utils/typeboxHelpers) applied to native edits, ensuring consistency with Instatic's security guarantees.
Atomic Commit and Publishing
When you confirm the import, the entire operation wraps in a single database transaction managed through the repository layer (server/repositories/*). The steps execute atomically:
- Insert new page rows via
insertPagemutations - Save design token mappings
- Update the site snapshot
- Bump the publish version in
server/publish/publishState.ts
Because this is a single transaction, any failure triggers an automatic rollback, preventing partial imports. Upon success, the static cache refreshes to reflect the new content.
Full Undo Support via Yjs CRDT
The import registers as a single undo step in the Yjs CRDT history. If you need to reverse the operation, pressing Undo restores the site to its exact pre-import state without residual artifacts. This applies to the entire import batch, regardless of how many individual pages or assets were processed.
Programmatic Import Using the Internal API
You can trigger imports programmatically using the internal API consumed by the UI. The main entry point accepts a map of file paths to Uint8Array buffers:
// src/core/import/superImport.ts – main entry
import { parseHtmlDocument } from '@core/utils/htmlParse';
import { createNodeTreeFromHtml, insertPage } from '@core/page-tree';
import { registerMedia } from '@core/plugins/mediaStorageRegistry';
import { db } from '@core/db/client';
export async function importStaticSite(files: Record<string, Uint8Array>) {
// Parse HTML files into NodeTrees
const pages = Object.entries(files)
.filter(([p]) => p.endsWith('.html'))
.map(([p, data]) => {
const doc = parseHtmlDocument(new TextDecoder().decode(data));
return { path: p, tree: createNodeTreeFromHtml(doc) };
});
// Extract CSS into design tokens
const tokenMap = await extractDesignTokens(files);
// Register media assets
await Promise.all(
Object.entries(files)
.filter(([p]) => /\.(png|jpe?g|svg|woff2?)$/.test(p))
.map(([p, data]) => registerMedia(p, data))
);
// Atomic transaction commit
await db.transaction(async (tx) => {
for (const { path, tree } of pages) {
await insertPage(tx, { slug: path.replace('.html', ''), tree });
}
await tokenMap.save(tx);
});
}
To add import functionality to a custom UI component:
// src/admin/pages/site/ui/ImportPanel.tsx
import { useDropzone } from '@ui/hooks/useDropzone';
import { importStaticSite } from '@core/import/superImport';
import { pushToast } from '@ui/components/Toast';
export function ImportPanel() {
const { getRootProps, getInputProps } = useDropzone({
onDrop: async (files) => {
try {
await importStaticSite(files);
pushToast({ kind: 'success', title: 'Site imported' });
} catch (e) {
pushToast({ kind: 'error', title: 'Import failed', body: e.message });
}
},
});
return (
<div {...getRootProps()} className="import-panel">
<input {...getInputProps()} />
<p>Drop a folder containing your static site here.</p>
</div>
);
}
Summary
- The Super Import engine in
src/core/import/superImport.tshandles the complete transformation of static files into Instatic data models - Imports convert HTML to
NodeTreestructures, CSS to Core Framework design tokens, and assets to registered media - Conflict detection in
server/writePolicy/siteDiff.tsprevents accidental overwrites through pre-commit diffing - All imports execute as atomic transactions with automatic rollback on failure, followed by a publish version bump
- Full undo support via Yjs CRDT allows complete reversal of import operations without residual data
Frequently Asked Questions
Can I import a static site programmatically without using the drag-and-drop UI?
Yes. The importStaticSite function exported from src/core/import/superImport.ts accepts a Record<string, Uint8Array> representing file paths and their binary content. You can invoke this directly from server-side scripts or custom admin components, passing the files map to trigger the same parsing, validation, and atomic commit process used by the UI.
How does Instatic handle CSS conflicts during an import?
The importer uses server/writePolicy/siteDiff.ts to compute a diff between incoming and existing design tokens. If color values or style rule names collide, the UI presents options to overwrite the existing token, keep both by renaming the new entry, or skip the conflicting item entirely. This ensures your existing design system remains intact unless you explicitly choose to modify it.
Is the import operation reversible if I make a mistake?
Yes. The import registers as a single history event in the Yjs CRDT system. Pressing Undo in the editor reverts the database to its exact pre-import state, removing all inserted pages, tokens, and media references. Because the operation uses atomic transactions, there is no risk of partial remnants remaining after an undo.
What file types are supported for media import during the process?
The Super Import engine automatically registers files matching /\.(png|jpe?g|svg|woff2?)$/ and similar asset patterns. These files are processed through @core/plugins/mediaStorageRegistry and become available in the Media workspace. Unsupported file types are ignored during the import but will not cause the transaction to fail.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →