Integration Guide
This guide covers the essentials for integrating Extable into your application.
Integration
Getting Started
Start with the Basic Usage Demo to understand initialization and basic setup.
See also:
- Data Format Guide - Schema definition and supported types
- Data Access from API - Async data fetching patterns
- Init Options Reference - Table initialization options
Server-side Rendering (SSR)
@extable/core/ssr generates static HTML for initial render. The HTML is not hydrated by the React wrapper; client-side rendering will re-create the table. Treat SSR as a fast, SEO-friendly snapshot, and let the client mount replace it.
Next.js (pages router) example
import { renderTableHTML } from "@extable/core/ssr";
export async function getServerSideProps() {
const result = renderTableHTML({
data,
schema,
cssMode: "both",
wrapWithRoot: true,
defaultClass: "extable",
includeStyles: true,
});
return {
props: {
ssrHtml: result.html,
ssrCss: result.css ?? "",
},
};
}
export default function Page({ ssrHtml, ssrCss }) {
return (
<>
{ssrCss && <style dangerouslySetInnerHTML={{ __html: ssrCss }} />}
<div dangerouslySetInnerHTML={{ __html: ssrHtml }} />
{/* Client render: use Extable (React wrapper) or ExtableCore in a separate container */}
</>
);
}Client re-render note
When you mount the client table, it will build its own DOM. To avoid showing two tables, either:
- Replace the SSR container on mount (clear its innerHTML and mount the client table there), or
- Keep SSR HTML in a separate container and hide/remove it after the client table mounts.
If you need true DOM hydration, a dedicated hydration API would be required (out of scope for the current SSR MVP).
Shortcut Key Registration
Register keyboard shortcuts for Undo/Redo operations.
import { ExtableCore } from "@extable/core";
const table = new ExtableCore({
root: container,
schema,
defaultData,
defaultView: {},
});
// Register keyboard handler for Ctrl+Z / Ctrl+Shift+Z (undo/redo)
const onKey = (e: KeyboardEvent) => {
const key = e.key.toLowerCase();
const isMod = e.metaKey || e.ctrlKey;
if (!isMod) return;
// Undo: Ctrl/Cmd+Z
if (key === "z") {
if (!table) return;
e.preventDefault();
e.stopPropagation();
if (e.shiftKey) {
table.redo(); // Ctrl/Cmd+Shift+Z
} else {
table.undo(); // Ctrl/Cmd+Z
}
}
};
document.addEventListener("keydown", onKey, { capture: true });
window.addEventListener("beforeunload", () => {
document.removeEventListener("keydown", onKey, { capture: true });
});Keyboard Shortcuts:
- Ctrl/Cmd+Z - Undo last change
- Ctrl/Cmd+Shift+Z - Redo last undone change
See Data Access API for undo/redo details.
Layout & Responsive Design
The table container must have explicit dimensions. Below are patterns for each framework with HTML, class/style configurations, and corresponding CSS or Tailwind utilities.
The table displays to fill its container (#table-root). Set explicit dimensions on the container and parent using flexbox:
<div class="app">
<aside class="sidebar"><!-- Menu --></aside>
<div class="main">
<div class="toolbar"><!-- Controls --></div>
<div id="table-root"></div>
</div>
</div>const core = new ExtableCore({
root: document.getElementById("table-root")!,
defaultData: data,
schema: schema,
options: {
// Optional: apply default classes/styles
defaultClass: "table-container",
defaultStyle: { border: "1px solid #e0e0e0" },
},
});CSS:
.app {
display: flex;
height: 100vh;
}
.sidebar {
width: 250px;
border-right: 1px solid #e0e0e0;
overflow-y: auto;
}
.main {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
.toolbar {
padding: 8px 12px;
border-bottom: 1px solid #e0e0e0;
flex-shrink: 0;
}
#table-root {
flex: 1;
min-height: 0;
}
.table-container {
/* Applied via options.defaultClass */
}Container Requirements
- Explicit
widthandheight(notauto) min-width: 0andmin-height: 0in flex/grid layouts- Table responds automatically to container size changes
Customization
- Validation - Schema-based constraints
- Edit Modes - Protect specific cells/columns
- Formulas - Add computed columns
- Conditional Style - Style cells dynamically
- Styling - Theme and appearance
Interaction Design
Choose the interaction pattern based on your application's requirements.
See Data Access API for complete API reference.
Read-only Mode
Users can view and search data, but cannot make edits.
const table = new ExtableCore({
root: container,
schema,
defaultData,
defaultView: {},
options: {
editMode: "readonly", // Disable all editing
},
});Use cases: Reports, dashboards, audit logs.
Direct Mode
Edits are applied immediately and require no user confirmation. Changes are sent to server immediately (if configured).
const table = new ExtableCore({
root: container,
schema,
defaultData,
defaultView: {},
options: {
editMode: "direct", // Default: changes apply immediately
},
});
// Listen for individual row changes
table.subscribeRowState((rowId, event) => {
if (event === "edit") {
console.log(`Row ${rowId} was edited - send to server`);
} else if (event === "new") {
console.log(`Row ${rowId} was inserted`);
} else if (event === "delete") {
console.log(`Row ${rowId} was deleted`);
}
});Use cases: Quick-edit forms, live dashboards, immediate feedback systems.
Commit Mode
Edits are queued as pending changes. User must explicitly commit to confirm all changes at once.
const table = new ExtableCore({
root: container,
schema,
defaultData,
defaultView: {},
options: {
editMode: "commit", // Require explicit commit
},
});
// Monitor pending changes to update UI
table.subscribeTableState((state) => {
const saveButton = document.getElementById("save-btn");
// Enable save button only when there are pending changes
saveButton!.disabled = !state.canCommit;
console.log(`${state.pendingCellCount} cells pending`);
});When user clicks "Save" / "Submit" / "Confirm" button (implemented by your app):
async function handleSave() {
try {
const changes = await table.commit();
// Send delta to server
const response = await fetch("/api/table/sync", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ changes }),
});
if (response.ok) {
console.log("Changes saved successfully");
}
} catch (error) {
console.error("Save failed:", error);
}
}Use cases: Form workflows, bulk imports, transactional updates, audit trails.
Testing
For unit tests and E2E testing strategies, see the Unit Testing Guide.