# Workflow for Importing an Existing Static Site into Instatic: Complete Guide

> Learn the workflow for importing an existing static site into Instatic. This guide transforms your HTML, CSS, and assets into editable nodes with Instatic's atomic pipeline.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: how-to-guide
- Published: 2026-08-02

---

**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`](https://github.com/CoreBunch/Instatic/blob/main/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`](https://github.com/CoreBunch/Instatic/blob/main/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 `inlineStyles` records
- **Linked stylesheets** are parsed into structured `StyleRule` objects
- **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`](https://github.com/CoreBunch/Instatic/blob/main/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:

1. Insert new page rows via `insertPage` mutations
2. Save design token mappings
3. Update the site snapshot
4. Bump the publish version in [`server/publish/publishState.ts`](https://github.com/CoreBunch/Instatic/blob/main/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:

```typescript
// 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:

```tsx
// 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.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/import/superImport.ts) handles the complete transformation of static files into Instatic data models
- Imports convert HTML to `NodeTree` structures, CSS to **Core Framework** design tokens, and assets to registered media
- **Conflict detection** in [`server/writePolicy/siteDiff.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/writePolicy/siteDiff.ts) prevents 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`](https://github.com/CoreBunch/Instatic/blob/main/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`](https://github.com/CoreBunch/Instatic/blob/main/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.