logo
  • Docs
  • API Reference
    Introduction
    What is Lix?
    Getting Started
    Comparison to Git
    Lix for AI Agents
    Essentials
    How Lix Works
    Querying Changes
    Data Model
    Plugins
    Persistence
    Guides
    Versions (Branching)
    History
    Diffs
    Attribution (Blame)
    Change Proposals
    Validation Rules
    Undo/Redo
    Restore
    Conversations
    Labels
    Key-Value Store
    Environment API
    Testing
    React Integration
    Logging & Debugging
    Deterministic Mode
    Metadata
    Writer Key
    Architecture
    Lix as File Format
    Previous pageWhat is Lix?Next pageComparison to Git

    #Getting Started

    Build a simple app that tracks JSON file changes with Lix.

    #Install

    npm
    yarn
    pnpm
    bun
    deno
    npm install @lix-js/sdk @lix-js/plugin-json
    yarn add @lix-js/sdk @lix-js/plugin-json
    pnpm add @lix-js/sdk @lix-js/plugin-json
    bun add @lix-js/sdk @lix-js/plugin-json
    deno add npm:@lix-js/sdk npm:@lix-js/plugin-json

    #Step 1: Open a Lix with Plugins

    Open a Lix with plugins for the file formats you want to track. Plugins teach Lix what constitutes a meaningful change in each format.

    NOTE

    In this example, we're using the default in-memory mode. For persistence to disk, cloud storage, or browser storage, see Persistence.

    import { openLix, createCheckpoint  } from "@lix-js/sdk";
    import { plugin as jsonPlugin } from "@lix-js/plugin-json";
    
    const lix = await openLix({
      providePlugins: [jsonPlugin],
    });

    Without plugins, Lix treats files as binary blobs. The jsonPlugin enables entity-level tracking by detecting that the age property changed from 50 to 51, rather than just "line 3 changed."

    #Step 2: Insert and Update Files

    Files are stored as Uint8Array (binary data), making Lix format-agnostic. Once inserted, every change is automatically tracked.

    Insert a file:

    import { openLix, createCheckpoint  } from "@lix-js/sdk";
    import { plugin as jsonPlugin } from "@lix-js/plugin-json";
    
    const lix = await openLix({
      providePlugins: [jsonPlugin],
    });
    const json = {
      name: "Peter",
      age: 50,
    };
    
    await lix.db
      .insertInto("file")
      .values({
        path: "/example.json",
        data: new TextEncoder().encode(JSON.stringify(json)),
      })
      .execute();
    
    const firstCheckpoint = await createCheckpoint({ lix });

    Update the file:

    import { openLix, createCheckpoint  } from "@lix-js/sdk";
    import { plugin as jsonPlugin } from "@lix-js/plugin-json";
    
    const lix = await openLix({
      providePlugins: [jsonPlugin],
    });
    
    const json = {
      name: "Peter",
      age: 50,
    };
    
    await lix.db
      .insertInto("file")
      .values({
        path: "/example.json",
        data: new TextEncoder().encode(JSON.stringify(json)),
      })
      .execute();
    
    const firstCheckpoint = await createCheckpoint({ lix });
    // we update the user's age to 51
    await lix.db
      .updateTable("file")
      .where("path", "=", "/example.json")
      .set({
        data: new TextEncoder().encode(
          JSON.stringify({ name: "Peter", age: 51 }),
        ),
      })
      .execute();
    
    const secondCheckpoint = await createCheckpoint({ lix });

    The JSON plugin detects precisely what changed (in this case, the age property from 50 to 51) while preserving all previous states.

    #Step 3: Query History

    Query file state at any point in time using SQL and checkpoints:

    import { openLix, createCheckpoint  } from "@lix-js/sdk";
    import { plugin as jsonPlugin } from "@lix-js/plugin-json";
    
    const lix = await openLix({
      providePlugins: [jsonPlugin],
    });
    
    const json = {
      name: "Peter",
      age: 50,
    };
    
    await lix.db
      .insertInto("file")
      .values({
        path: "/example.json",
        data: new TextEncoder().encode(JSON.stringify(json)),
      })
      .execute();
    
    const firstCheckpoint = await createCheckpoint({ lix });
    
    // we update the user's age to 51
    await lix.db
      .updateTable("file")
      .where("path", "=", "/example.json")
      .set({
        data: new TextEncoder().encode(
          JSON.stringify({ name: "Peter", age: 51 }),
        ),
      })
      .execute();
    
    const secondCheckpoint = await createCheckpoint({ lix });
    // Query file history at each checkpoint
    for (const { label, checkpoint } of [
      { label: "Second checkpoint", checkpoint: secondCheckpoint },
      { label: "First checkpoint", checkpoint: firstCheckpoint },
    ]) {
      const fileState = await lix.db
        .selectFrom("file_history")
        .where("path", "=", "/example.json")
        .where("lixcol_depth", "=", 0)
        .where("lixcol_root_commit_id", "=", checkpoint.id)
        .select(["path", "data"])
        .executeTakeFirstOrThrow();
    
      console.log(`${label}:`, {
        ...fileState,
        data: JSON.parse(new TextDecoder().decode(fileState.data)),
      });
    }

    The file_history view reconstructs file state by walking backwards from a commit (lixcol_root_commit_id). Use lixcol_depth = 0 to get the exact state at that checkpoint.

    TIP

    Lix automatically commits every change. Checkpoints are optionally labelled commits for important moments like before/after a migration or deployment.

    #Next Steps

    Track changes in your app:

    • Versions - Create isolated versions (branching) for parallel work
    • Change Proposals - Review and approve changes before merging
    • Querying Changes - Advanced SQL queries for diffs and attribution

    Add more file types:

    • Plugins - CSV, Markdown, or create custom plugins