@usesidekick/react
v0.2.3
Published
Zero-change runtime for extensible React applications. Override modules can wrap, replace, or enhance any component by name without modifying the host app's source code.
Downloads
1,184
Readme
@usesidekick/react
Zero-change runtime for extensible React applications. Override modules can wrap, replace, or enhance any component by name without modifying the host app's source code.
Installation
npm install @usesidekick/reactFor the full automated setup (installs deps, configures build, creates API routes):
npx @usesidekick/cli initQuick Start
1. Wrap Your App
import { SidekickProvider } from '@usesidekick/react';
export default function App() {
return (
<SidekickProvider overridesEndpoint="/api/sidekick/overrides">
<YourApp />
</SidekickProvider>
);
}2. Add the Build Plugin
The babel-plugin-add-react-displayname plugin preserves component names through minification so overrides can target them in production.
npm install -D babel-plugin-add-react-displaynameNext.js (.babelrc):
{
"presets": [
["next/babel", {
"preset-react": {
"runtime": "automatic",
"importSource": "@usesidekick/react"
}
}]
],
"plugins": ["add-react-displayname"]
}Vite (vite.config.ts):
import react from '@vitejs/plugin-react';
export default {
plugins: [
react({
jsxImportSource: '@usesidekick/react',
babel: { plugins: ['add-react-displayname'] }
})
]
}3. Write an Override
import { createOverride } from '@usesidekick/react';
export default createOverride({
name: 'Beta Banner',
primitives: ['ui.wrap'],
activate: (sdk) => {
sdk.ui.wrap('TaskBoard', (Original) => (props) => (
<div>
<div style={{ background: 'purple', color: 'white', padding: 8 }}>Beta</div>
<Original {...props} />
</div>
));
},
});Override Primitives
UI
| Method | Description |
|--------|-------------|
| sdk.ui.wrap(name, wrapper) | Wrap a component with additional markup/logic |
| sdk.ui.replace(name, component) | Replace a component entirely |
| sdk.ui.setText(selector, text) | Set the text content of matching elements |
| sdk.ui.inject(extensionPointId, component) | Inject into an <ExtensionPoint> slot |
| sdk.ui.addStyles(css) | Inject global CSS |
| sdk.ui.addColumn(tableId, config) | Add a column to a table |
| sdk.ui.addMenuItem(menuId, config) | Add a menu item |
| sdk.ui.addTab(tabGroupId, config) | Add a tab |
| sdk.ui.addAction(actionBarId, config) | Add an action button |
| sdk.ui.modifyProps(componentId, modifier) | Modify a component's props at render time |
Data
| Method | Description |
|--------|-------------|
| sdk.data.computed(fieldName, compute) | Add a computed/derived field |
| sdk.data.addFilter(name, filter) | Add a data filter |
| sdk.data.transform(dataKey, transform) | Transform data before render |
| sdk.data.intercept(pathPattern, handler) | Intercept API responses |
| sdk.data.addSortOption(tableId, config) | Add a sort option |
| sdk.data.addGroupBy(tableId, config) | Add a group-by option |
Behavior
| Method | Description |
|--------|-------------|
| sdk.behavior.onEvent(name, handler) | Listen for custom events |
| sdk.behavior.addKeyboardShortcut(keys, action) | Register keyboard shortcuts |
| sdk.behavior.modifyRoute(pattern, handler) | Modify routing behavior |
React Hooks
The SDK provides hooks for host apps that want to read override state:
import {
useAddedColumns,
useColumnRenames,
useHiddenColumns,
useColumnOrder,
useMenuItems,
useTabs,
useActions,
useValidations,
usePropsModifier,
useComputedField,
useFilter,
useSortOptions,
useGroupByOptions,
useKeyboardShortcuts,
useEventEmitter,
} from '@usesidekick/react';SidekickPanel
A built-in admin panel for managing overrides at runtime. Supports AI-powered generation, toggling, and deletion.
import { SidekickPanel } from '@usesidekick/react';
<SidekickPanel
apiEndpoint="/api/sidekick/generate"
toggleEndpoint="/api/sidekick/toggle"
deleteEndpoint="/api/sidekick/delete"
/>| Prop | Type | Description |
|------|------|-------------|
| apiEndpoint | string? | Endpoint for AI override generation |
| toggleEndpoint | string? | Endpoint for enable/disable |
| deleteEndpoint | string? | Endpoint for deletion |
| onGenerate | function? | Custom generate handler (overrides apiEndpoint) |
| onToggle | function? | Custom toggle handler (overrides toggleEndpoint) |
| onDelete | function? | Custom delete handler (overrides deleteEndpoint) |
Server (@usesidekick/react/server)
Server-side utilities for building the Sidekick API backend. Handles override CRUD, AI generation, and validation.
Default Setup (JSON File Storage)
Zero-dependency local storage — no database required:
// app/api/sidekick/[...action]/route.ts
import { createSidekickHandler, createJsonFileStorage } from '@usesidekick/react/server';
export const dynamic = 'force-dynamic';
const handler = createSidekickHandler({
storage: createJsonFileStorage(),
});
export const GET = handler.GET;
export const POST = handler.POST;Overrides are stored in .sidekick/overrides.json by default. You can pass a custom path:
createJsonFileStorage('/path/to/overrides.json')Drizzle + Neon Setup (@usesidekick/react/server/drizzle)
For production deployments that need a real database:
// app/api/sidekick/[...action]/route.ts
import { createSidekickHandler } from '@usesidekick/react/server';
import { createDrizzleStorage } from '@usesidekick/react/server/drizzle';
import { db } from '@/lib/db';
import { overrides } from '@/lib/db/schema';
const handler = createSidekickHandler({
storage: createDrizzleStorage(db, overrides),
});
export const GET = handler.GET;
export const POST = handler.POST;This single catch-all route handles four actions (dispatched by the last URL path segment):
| Method | Path | Action |
|--------|------|--------|
| GET | /api/sidekick/overrides | List all overrides |
| POST | /api/sidekick/generate | AI-generate an override |
| POST | /api/sidekick/toggle | Enable/disable an override |
| POST | /api/sidekick/delete | Delete an override |
Server Exports
// Core handler + JSON storage
import {
createSidekickHandler,
createJsonFileStorage,
} from '@usesidekick/react/server';
// Drizzle adapter (separate import — no drizzle-orm dependency pulled unless used)
import {
createDrizzleStorage,
sidekickOverrides, // pgTable definition (for your schema)
} from '@usesidekick/react/server/drizzle';Custom Storage Adapter
Implement SidekickStorage to use any database:
import type { SidekickStorage } from '@usesidekick/react/server';
const myStorage: SidekickStorage = {
listOverrides: async () => { /* ... */ },
getOverride: async (id) => { /* ... */ },
createOverride: async (data) => { /* ... */ },
updateOverride: async (id, data) => { /* ... */ },
deleteOverride: async (id) => { /* ... */ },
};
const handler = createSidekickHandler({ storage: myStorage });How It Works
The SDK overrides React.createElement via a custom JSX runtime to intercept every component render. When a component renders, it checks:
- Is there a replacement registered for this component? Use it.
- Is there a wrapper registered? Wrap the original.
- Otherwise, render normally.
This requires zero changes to existing components.
Component Name Resolution
Names are resolved in order: displayName > function.name > inner component name (for memo/forwardRef).
| Environment | Name Source | Reliable? | |-------------|-------------|-----------| | Development | function name | Yes | | Production + build plugin | auto-added displayName | Yes | | Production without plugin | minified name | No |
Performance
- One
Map.get()percreateElementcall - No overhead when no overrides are registered
Compatibility
Works with: functional components, class components, React.memo, React.forwardRef, Suspense, Portals.
Not supported: React Server Components (client-side only).
Package Exports
| Import Path | Description |
|-------------|-------------|
| @usesidekick/react | Provider, hooks, panel, override API, types |
| @usesidekick/react/jsx-runtime | Custom JSX runtime (configured via jsxImportSource) |
| @usesidekick/react/jsx-dev-runtime | Development JSX runtime |
| @usesidekick/react/server | Server handler, JSON file storage, AI generation |
| @usesidekick/react/server/drizzle | Drizzle ORM storage adapter + pgTable schema |
Peer Dependencies
| Package | Version | Required? |
|---------|---------|-----------|
| react | ^18.2.0 | Yes |
| drizzle-orm | >=0.29.0 | Only if using createDrizzleStorage |
| next | >=14.0.0 | Only if using createSidekickHandler |
License
MIT
