Diffs

Lix provides APIs to query any two states of your data—the "before" and "after"—which is the foundation for showing users what has changed. These states can be from different points in history or different versions.

Diff

Rendering Diffs

Lix's responsibility is to provide the before and after states and/or changes between states. Lix relies on plugins to provide the UI for rendering diffs, or developers to implement their own rendering logic.

Plugin Provided Diffs

Lix plugins can provide ready-to-use diff components. These components can be rendered directly in your application, offering a quick way to display changes.

However, this convenience comes with a trade-off: you are limited to the UI and rendering logic provided by the plugin. If you need more control over the appearance or behavior of the diffs, you may want to consider other options.

import { openLix } from "@lix-js/sdk";
const plugin = (await lix.plugin.getAll()).find(
  (p: any) => p.key === "csv_plugin"
);

if (plugin?.diffUiComponent) {
  // In a real application, you would define the custom element:
  // customElements.define("csv-diff-component", plugin.diffComponent);
  // Then render: <csv-diff-component diffs={changes} />;
  console.log("Plugin diff component available:", !!plugin.diffUiComponent);
} else {
  console.log("No diff component found for CSV plugin");
}

Diff Libraries

You can use diff libraries to visualize changes in your data. Popular options include general-purpose libraries like diff for text-based comparisons, or specialized solutions like the Lix HTML diff package.

The Lix HTML diff is particularly powerful because it leverages the fact that web applications render to HTML. This means that, in theory, the HTML diff can be universally used to visualize changes in any web app's UI—regardless of what is rendered by diffing the rendered HTML output.

Build Your Own

You always have the flexibility to implement your own diff rendering logic for complete control over how changes are detected and displayed.

Lix is unopinionated about how you visualize changes. Because Lix provides "before" and "after" states (or changes in between states), you can build any kind of diff UI you can imagine—from text-based diffs to visual comparisons of images, designs, or even 3D models, as illustrated below.

Diffs come in many different types

Code Examples

Diffing a JSON file

The following example shows how to query two versions of a JSON file and then generate a simple textual diff.

import { openLix } from "@lix-js/sdk";

const plugin = (await lix.plugin.getAll()).find(
  (p: any) => p.key === "csv_plugin"
);

if (plugin?.diffUiComponent) {
  // In a real application, you would define the custom element:
  // customElements.define("csv-diff-component", plugin.diffComponent);
  // Then render: <csv-diff-component diffs={changes} />;
  console.log("Plugin diff component available:", !!plugin.diffUiComponent);
} else {
  console.log("No diff component found for CSV plugin");
}
// 1. Get the current state of a JSON file from the getting-started example
const currentFile = await lix.db
  .selectFrom("file")
  .where("path", "=", "/example.json")
  .selectAll()
  .executeTakeFirst();

if (currentFile) {
  const currentData = JSON.parse(new TextDecoder().decode(currentFile.data));
  console.log("Current file data:", currentData);

  // For demo purposes, create a mock previous version
  const previousData = { name: "Peter", age: 50 };

  const afterState = currentData;
  const beforeState = previousData;

  // 2. Compare the two states to generate a diff.
  //
  // This is a simplified example. In a real app,
  // you would likely use a diffing library.
  const diffOutput: string[] = [];
  if (beforeState.name !== afterState.name) {
    diffOutput.push(`- name: ${beforeState.name}`);
    diffOutput.push(`+ name: ${afterState.name}`);
  }
  if (beforeState.age !== afterState.age) {
    diffOutput.push(`- age: ${beforeState.age}`);
    diffOutput.push(`+ age: ${afterState.age}`);
  }

  // 3. The diff can then be displayed in your application.
  console.log("Diff output:");
  console.log(diffOutput.join("\n"));
} else {
  console.log(
    "File /example.json not found - run getting-started example first"
  );
}

Diffing a single entity

This example shows how to diff a single entity. While the example uses a JSON property, the same principle applies to any entity defined by a Lix plugin, such as a paragraph in a Markdown document or a cell in a CSV file. Diffing at the entity level is more efficient than diffing an entire file when you only care about a specific part of the data.

import { openLix } from "@lix-js/sdk";

const plugin = (await lix.plugin.getAll()).find(
  (p: any) => p.key === "csv_plugin"
);

if (plugin?.diffUiComponent) {
  // In a real application, you would define the custom element:
  // customElements.define("csv-diff-component", plugin.diffComponent);
  // Then render: <csv-diff-component diffs={changes} />;
  console.log("Plugin diff component available:", !!plugin.diffUiComponent);
} else {
  console.log("No diff component found for CSV plugin");
}

// 1. Get the current state of a JSON file from the getting-started example
const currentFile = await lix.db
  .selectFrom("file")
  .where("path", "=", "/example.json")
  .selectAll()
  .executeTakeFirst();

if (currentFile) {
  const currentData = JSON.parse(new TextDecoder().decode(currentFile.data));
  console.log("Current file data:", currentData);

  // For demo purposes, create a mock previous version
  const previousData = { name: "Peter", age: 50 };

  const afterState = currentData;
  const beforeState = previousData;

  // 2. Compare the two states to generate a diff.
  //
  // This is a simplified example. In a real app,
  // you would likely use a diffing library.
  const diffOutput: string[] = [];
  if (beforeState.name !== afterState.name) {
    diffOutput.push(`- name: ${beforeState.name}`);
    diffOutput.push(`+ name: ${afterState.name}`);
  }
  if (beforeState.age !== afterState.age) {
    diffOutput.push(`- age: ${beforeState.age}`);
    diffOutput.push(`+ age: ${afterState.age}`);
  }

  // 3. The diff can then be displayed in your application.
  console.log("Diff output:");
  console.log(diffOutput.join("\n"));
} else {
  console.log(
    "File /example.json not found - run getting-started example first"
  );
}
// Get current entities from the state table to demonstrate entity diffing
const currentEntities = await lix.db
  .selectFrom("state")
  .selectAll()
  .limit(2)
  .execute();

if (currentEntities.length >= 2) {
  console.log("Comparing two entities from the current state:");
  console.log(
    `Entity 1: ${currentEntities[0].entity_id} (${currentEntities[0].schema_key})`
  );
  console.log(
    `Entity 2: ${currentEntities[1].entity_id} (${currentEntities[1].schema_key})`
  );

  // Show their content differences
  console.log("Entity 1 content:", currentEntities[0].snapshot_content);
  console.log("Entity 2 content:", currentEntities[1].snapshot_content);
} else {
  console.log("Not enough entities to compare - need at least 2 entities");
}