+
+// After: EditableTable
+
+```
+
+### Adding Validation
+
+```tsx
+// 1. Define schema
+const schema = {
+ name: z.string().min(1, 'Name is required'),
+ quantity: z.coerce.number().min(0, 'Quantity must be positive'),
+};
+
+// 2. Create validation handler
+const getValidateHandler = (key) => async ({ value }) => {
+ const result = schema[key]?.safeParse(value);
+ return result?.success ? null : result.error.errors[0]?.message;
+};
+
+// 3. Add to table
+
+```
+
+### Adding Save Functionality
+
+```tsx
+// 1. Create save handler
+const getSaveHandler = (key) => async ({ value, data }) => {
+ try {
+ await updateRecord(data.id, { [key]: value });
+ return null; // Success
+ } catch (error) {
+ return error.message; // Error
+ }
+};
+
+// 2. Add to table
+
+```
+
+## Conclusion
+
+The EditableTable component represents a sophisticated solution for inline data editing in admin interfaces. It successfully combines:
+
+- **Developer Experience**: Type-safe, well-documented API with clear patterns
+- **User Experience**: Intuitive editing with immediate feedback and error handling
+- **Performance**: Optimized for large datasets with efficient rendering
+- **Maintainability**: Clean architecture with separation of concerns
+- **Extensibility**: Plugin-ready design for future enhancements
+
+The InventoryTable implementation demonstrates how these patterns can be applied to real-world scenarios, providing a template for future table implementations throughout the Medusa2 admin interface.
+
+**Key Takeaways:**
+- **⚠️ CRITICAL: Never depend on `data` state in handlers** - use `table` instance to avoid re-renders
+- **⚠️ CRITICAL: Never call validation manually in save handler** - it's automatic
+- **Type all handlers** with `EditableCellActionFn` for type safety
+- Use schema-driven validation for consistency and type safety
+- Implement proper error handling and user feedback
+- Leverage URL state for better user experience
+- Design for performance from the start
+- Maintain clear separation between data, validation, and save logic
+- **Always memoize column definitions inside custom hooks** to prevent re-render issues
+- **Use `useCallback` for all handler functions** with empty dependencies `[]`
+- **Never call hooks inside other hooks** to avoid React violations
+- **Test status indicators** to verify cells maintain state during saves
+
+## Critical Performance Patterns
+
+### The "Status Indicator Test"
+Before deploying any EditableTable implementation, perform this test:
+
+1. **Edit a cell** and observe the status indicators
+2. **Verify the complete flow**: Editing → Saving → Success/Error
+3. **Check for state resets**: Status indicators should not disappear during saves
+4. **Test multiple cells**: Only the edited cell should show indicators
+
+If status indicators don't work correctly, the table has re-render issues that need to be fixed using the patterns outlined in this guide.
+
+This component serves as a foundation for efficient data management workflows and can be extended to support additional use cases as the admin interface evolves.
diff --git a/packages/medusa-forms/src/editable-table/columnHelpers.tsx b/packages/medusa-forms/src/editable-table/columnHelpers.tsx
new file mode 100644
index 0000000..ba0ca47
--- /dev/null
+++ b/packages/medusa-forms/src/editable-table/columnHelpers.tsx
@@ -0,0 +1,59 @@
+import type { ColumnDef } from '@tanstack/react-table';
+import type { EditableTableCellMeta } from './types/cells';
+import type { EditableColumnType } from './types/columns';
+
+export const canSortColumn = (type: EditableColumnType) => ['text', 'number'].includes(type);
+
+// Get default column sizing based on field type
+export function getDefaultColumnSizing(type: EditableColumnType): number {
+ switch (type) {
+ case 'number':
+ return 120;
+ case 'badge':
+ return 100;
+ case 'select':
+ case 'autocomplete':
+ return 180;
+ default:
+ return 200;
+ }
+}
+
+// Get filter function based on field type
+export function getFilterFunction(type: string) {
+ switch (type) {
+ case 'boolean':
+ return 'equals';
+ case 'number':
+ return 'includesString'; // Use includesString for now, can be customized later
+ case 'date':
+ return 'includesString'; // Use includesString for now, can be customized later
+ default:
+ return 'includesString';
+ }
+}
+
+// Get sorting function based on field type
+export function getSortingFunction(type: string) {
+ switch (type) {
+ case 'number':
+ return 'basic';
+ case 'date':
+ return 'datetime';
+ case 'boolean':
+ return 'basic';
+ default:
+ return 'alphanumeric';
+ }
+}
+
+// Helper to get column class names based on type
+export function getColumnHeaderClassName(colDef: ColumnDef>): string {
+ const baseClasses = 'flex items-center gap-2 text-left justify-between';
+
+ const meta = colDef.meta as EditableTableCellMeta;
+ switch (meta?.type) {
+ default:
+ return `${baseClasses}`;
+ }
+}
diff --git a/packages/medusa-forms/src/editable-table/components/EditableTable.tsx b/packages/medusa-forms/src/editable-table/components/EditableTable.tsx
new file mode 100644
index 0000000..61389f4
--- /dev/null
+++ b/packages/medusa-forms/src/editable-table/components/EditableTable.tsx
@@ -0,0 +1,87 @@
+import { Table, clx } from '@medusajs/ui';
+import type { ReactNode } from 'react';
+import { useEditableCellActions } from '../hooks/useEditableCellActions';
+import { useEditableTable } from '../hooks/useEditableTable';
+import type { CellActionsHandlerGetter, EditableTableConfig } from '../types/cells';
+import { EditableTableContent } from './EditableTableContent';
+import { EditableTableControls } from './EditableTableControls';
+import { TableSkeleton } from './TableSkeleton';
+
+interface EditableTableProps> extends Omit, 'getCellActions'> {
+ tableId?: string;
+ showControls?: boolean;
+ showPagination?: boolean;
+ showInfo?: boolean;
+ className?: string;
+ loading?: boolean;
+ getValidateHandler: CellActionsHandlerGetter;
+ getSaveHandler: CellActionsHandlerGetter;
+ getOptionsHandler: CellActionsHandlerGetter<{ label: string; value: unknown }[]>;
+ // Additional actions to render in table controls
+ additionalActions?: ReactNode;
+}
+
+// Main EditableTable component
+export function EditableTable>({
+ showControls = true,
+ showPagination = true,
+ showInfo = true,
+ className,
+ loading = false,
+ data,
+ getValidateHandler,
+ getSaveHandler,
+ getOptionsHandler,
+ additionalActions,
+ ...inputConfig
+}: EditableTableProps) {
+ const getCellActionsFn = useEditableCellActions({ getValidateHandler, getSaveHandler, getOptionsHandler });
+
+ const { table } = useEditableTable({
+ ...inputConfig,
+ data,
+ getCellActions: getCellActionsFn,
+ });
+
+ // Show skeleton if loading
+ if (loading) {
+ const columnCount = inputConfig.editableColumns?.length || 6;
+ return ;
+ }
+
+ return (
+