| name | rebase-studio |
| description | Guide for using and customizing the Rebase Studio developer tools layer. Use this skill when the user needs help with Studio dev tools (SQL/JS/RLS/Storage/Cron/Schema Visualizer/Branches/API Explorer/Logs), admin modes (content/studio/settings), Studio home page customization, bridge hooks, or Studio configuration. Studio is NOT the CMS — the CMS lives in @rebasepro/admin. |
Rebase Studio
Rebase Studio (@rebasepro/studio) is the developer tools layer for Rebase. It provides 9 built-in tools — SQL Console, JS Console, RLS Editor, Storage browser, Cron Jobs manager, Schema Visualizer, Branches manager, API Explorer, and Logs Explorer — accessible via the "Studio" mode toggle in the sidebar.
Overview
- 9 built-in dev tools — all lazy-loaded and code-split so they don't impact initial bundle size
- StudioHomePage — customizable landing page with tool cards grouped by section
- Studio Bridge — hooks that connect Studio tools to CMS data (collections, navigation, side panels)
Admin Modes (Tri-State)
IMPORTANT FOR AGENTS: The Studio uses a tri-state mode system: "content" | "studio" | "settings". It is NOT "developer" / "editor". These are the only valid values.
The admin mode is controlled by AdminModeController and persisted in localStorage under the key rebase-admin-mode. Default mode is "content".
Mode Values
| Mode | Description | Navigation Shows |
|---|
"content" | Clean CMS experience for editing data. Default mode. | Collections + admin entries (Users/Roles) |
"studio" | Developer tools and schema management. | Dev tool views + admin entries (Users/Roles) |
"settings" | Application settings and configuration. | Settings-related views |
Mode Controller API
import { useAdminModeController } from "@rebasepro/app";
interface AdminModeController {
mode: "content" | "studio" | "settings";
setMode: (mode: "content" | "studio" | "settings") => void;
}
function MyComponent() {
const adminModeController = useAdminModeController();
if (adminModeController.mode === "studio") {
}
adminModeController.setMode("content");
}
Drawer Mode Switch
When <RebaseStudio> is registered, the drawer automatically renders a segmented Content / Studio toggle. Clicking "Content" sets mode to "content" and navigates to the base path. Clicking "Studio" sets mode to "studio" and navigates to /s.
Effective Role Simulation
IMPORTANT FOR AGENTS: The hook is called useEffectiveRoleController(), NOT useEffectiveRole().
In Studio mode, developers can select an "effective role" to preview the application as a specific role would see it. The role is persisted in localStorage under rebase-effective-role.
import { useEffectiveRoleController } from "@rebasepro/app";
interface EffectiveRoleController {
effectiveRole: string | null;
setEffectiveRole: (role: string | null) => void;
}
function RoleSimulator() {
const { effectiveRole, setEffectiveRole } = useEffectiveRoleController();
setEffectiveRole("editor");
setEffectiveRole(null);
}
Studio Dev Tools
The Studio ships 9 built-in dev tools, all lazy-loaded (code-split) so they don't impact the initial bundle. Heavy dependencies (Monaco, @xyflow/react, dagre, pgsql-ast-parser) are only loaded when a tool is visited.
Tool Reference
| Tool Key | Component | Name | Group | Icon | Description |
|---|
"sql" | SQLEditor | SQL Console | Database | terminal | Execute raw SQL queries against the database |
"js" | JSEditor | JS Console | Compute | code | Run JavaScript with the Rebase SDK in a live sandbox |
"rls" | RLSEditor | RLS Policies | Database | ShieldCheck | Configure Row Level Security for fine-grained data access |
"storage" | StorageView | Storage | Storage | HardDrive | Browse, upload, and manage files in the storage bucket |
"cron" | CronJobsView | Cron Jobs | Compute | Clock | Monitor and manage scheduled background tasks |
"schema-visualizer" | SchemaVisualizer | Schema Visualizer | Database | Network | Interactive ERD showing tables, columns, and relationships |
"branches" | BranchesView | Branches | Database | GitBranch | Create and manage isolated database copies for development |
"api" | ApiExplorer | API Explorer | API | BookOpen | Interactive API documentation with live request testing |
"logs" | LogsExplorer | Logs Explorer | Database | Activity | Real-time system, query, and authentication logs |
IMPORTANT FOR AGENTS: The "schema" tool (collection editor) is NOT registered by <RebaseStudio>. It is auto-injected by <RebaseShell> when collectionEditor is enabled on <RebaseAdmin>. Do not try to register it manually.
Enabling/Disabling Tools
By default, all 9 tools are enabled. Use the tools prop on <RebaseStudio> to selectively enable a subset:
<RebaseStudio />
<RebaseStudio tools={undefined} />
<RebaseStudio tools={["sql", "rls", "storage"]} />
<RebaseStudio tools={["sql", "js", "rls", "storage", "cron", "schema-visualizer", "api", "logs"]} />
The tools prop accepts an array of tool key strings:
type ToolKey = "sql" | "js" | "rls" | "schema" | "storage" | "cron"
| "schema-visualizer" | "branches" | "api" | "logs";
const DEFAULT_TOOLS: ToolKey[] = [
"sql", "js", "rls", "storage", "cron",
"schema-visualizer", "branches", "api", "logs"
];
Direct Tool Imports (Advanced)
For advanced use cases where you need direct access to a tool component (e.g., embedding it outside the Studio), use deep imports:
import { SQLEditor } from "@rebasepro/studio/components/SQLEditor/SQLEditor";
WARNING FOR AGENTS: Do NOT import individual tools from @rebasepro/studio top-level. The index only exports RebaseStudio and StudioHomePage. Individual tools are intentionally excluded to preserve code splitting.
Frontend Composition
The Studio is mounted using the declarative composition API. All four components (<Rebase>, <RebaseAuth>, <RebaseAdmin>, <RebaseStudio>, <RebaseShell>) are purely declarative — they render nothing and only register configuration into the RebaseRegistry. <RebaseShell> then reads the registry and builds the actual UI.
import { useRebaseAuthController, RebaseAuth } from "@rebasepro/app";
import { Rebase } from "@rebasepro/app";
import { RebaseAdmin, RebaseShell } from "@rebasepro/admin";
import { useDataEnhancementPlugin } from "@rebasepro/plugin-ai";
import { RebaseStudio } from "@rebasepro/studio";
import React from "react";
import { createRebaseClient } from "@rebasepro/client";
import { collections } from "virtual:rebase-collections";
const API_URL = import.meta.env.VITE_API_URL || (import.meta.env.DEV ? "http://localhost:3001" : undefined);
export function App() {
const rebaseClient = React.useMemo(() => createRebaseClient({ baseUrl: API_URL }), []);
const authController = useRebaseAuthController({ client: rebaseClient });
const dataEnhancementPlugin = useDataEnhancementPlugin();
return (
<Rebase client={rebaseClient} authController={authController} plugins={[dataEnhancementPlugin]}>
<RebaseAuth/>
<RebaseAdmin collections={collections} collectionEditor={true}/>
<RebaseStudio tools={undefined} homePage={undefined} />
<RebaseShell title="My App"/>
</Rebase>
);
}
TypeScript Strict Props Warning
Under strict TypeScript checks, <RebaseStudio/> without props throws:
Type '{}' is missing the following properties from type '{ tools: any; homePage: any; }': tools, homePage
Pass tools={undefined} homePage={undefined} explicitly:
<RebaseStudio tools={undefined} homePage={undefined} />
Key Components
| Component | Package | Purpose | Renders UI? |
|---|
<Rebase> | @rebasepro/app | Root provider (client, auth, user management, plugins) | Yes (providers) |
<RebaseAuth> | @rebasepro/app | Authentication config (custom login view) | No — registers into registry |
<RebaseAdmin> | @rebasepro/admin | CMS config (collections, views, editor) | No — registers into registry |
<RebaseStudio> | @rebasepro/studio | Studio config (tools, home page) | No — registers into registry |
<RebaseShell> | @rebasepro/admin | App shell (drawer, navigation, routes, layout) | Yes — the actual UI |
Global Component Overrides (Swizzling)
Rebase allows you to override default UI components globally by passing a components prop to the <Rebase> provider. This implements Docusaurus-style component swizzling, supporting both Eject and Wrap patterns.
import { Rebase } from "@rebasepro/app";
import { MyAppBar } from "./MyAppBar";
<Rebase
client={rebaseClient}
components={{
// Eject Mode: Replace the built-in AppBar entirely
"Shell.AppBar": { Component: MyAppBar },
// Wrap Mode: Wrap the default LoginView, adding custom branding
"Auth.LoginView": {
// `OriginalComponent` is injected at runtime when `wrap: true`; the override
// slot's type does not model it, hence the annotation.
Component: (({ OriginalComponent, ...props }: {
OriginalComponent: React.ComponentType<Record<string, unknown>>
}) => (
<div className="custom-login-wrapper">
<div className="branding">Welcome to My Enterprise App</div>
<OriginalComponent {...props} />
</div>
)) as unknown as React.ComponentType<Record<string, unknown>>,
wrap: true
}
}}
>
{/* your app */}
…
</Rebase>
Overridable Component Scopes
App-scoped Components (AppComponentName)
These components can only be overridden at the root <Rebase> provider, as they represent global shell or utility structures.
"Shell.AppBar" — The header bar at the top of the page.
"Shell.Drawer" — The collapsible main sidebar drawer.
"Shell.DrawerNavigationItem" — Sidebar navigation link.
"Shell.DrawerNavigationGroup" — Sidebar navigation group header.
"HomePage" — The landing dashboard of the CMS (when in content mode).
"HomePage.CollectionCard" — Individual collection link card on the home page.
"Auth.LoginView" — The login screen overlay.
Collection-scoped Components (CollectionComponentName)
These components can be overridden globally on <Rebase> (which acts as a fallback for all collections) or overridden on individual collections inside their definitions.
"Collection.View" — The container view for the collection.
"Collection.Table" — The default tabular data view.
"Collection.Card" — Individual card wrapper.
"Collection.EmptyState" — View shown when a collection is empty.
"Collection.Actions" — Toolbar actions header.
"Entity.Form" — The detail form view.
"Entity.FormActions" — Form action button bar.
"Entity.DetailView" — Read-only detail view.
"Entity.SidePanel" — Side panel wrapper.
"Entity.Preview" — Reference / relation preview chip.
"Entity.MissingReference" — Placeholder view when a relation references a deleted/non-existent entity.
RebaseAdmin Configuration
<RebaseAdmin> accepts the full RebaseAdminConfig:
interface RebaseAdminConfig<EC extends CollectionConfig = CollectionConfig> {
collections?: EC[] | CollectionConfigsBuilder<EC>;
views?: AppView[] | AppViewsBuilder;
homePage?: ReactNode;
entityViews?: EntityCustomView[];
entityActions?: EntityAction[];
plugins?: RebasePlugin[];
navigationGroupMappings?: NavigationGroupMapping[];
collectionEditor?: boolean | CollectionEditorOptions;
}
Collection Editor Options
interface CollectionEditorOptions {
getAuthToken?: () => Promise<string | null>;
readOnly?: boolean;
pathSuggestions?: string[];
}
Navigation Group Mappings
Control how collections and views are grouped in the sidebar and home page:
interface NavigationGroupMapping {
name: string;
entries: string[];
collapsedByDefault?: boolean | {
drawer?: boolean;
home?: boolean;
};
}
<RebaseAdmin
collections={collections}
navigationGroupMappings={[
{ name: "Content", entries: ["posts", "pages", "media"] },
{ name: "Commerce", entries: ["products", "orders"], collapsedByDefault: { drawer: true } },
]}
/>
RebaseStudio Configuration
<RebaseStudio> accepts the full RebaseStudioConfig:
interface RebaseStudioConfig {
tools?: ("sql" | "js" | "rls" | "schema" | "storage" | "cron"
| "schema-visualizer" | "branches" | "api" | "logs")[];
homePage?: ReactNode;
devViews?: AppView[];
}
StudioHomePage Customization
The StudioHomePage component is the default landing page for Studio mode. It renders tool cards organized by section. It can be customized through props:
interface StudioHomePageProps {
additionalActions?: React.ReactNode;
additionalChildrenStart?: React.ReactNode;
additionalChildrenEnd?: React.ReactNode;
sections?: HomePageSection[];
hiddenGroups?: string[];
}
interface HomePageSection {
key: string;
title: string;
children: React.ReactNode;
}
Built-in Home Page Sections
The default StudioHomePage renders tools grouped into these sections:
| Section | Dot Color | Tools |
|---|
| Database | Emerald | Collections, Schema Visualizer, SQL Console, Branches, RLS Policies, Logs Explorer |
| Compute | Blue | JS Console, Cron Jobs |
| API | Violet | API Explorer |
| Storage | Amber | Storage |
| Access Control | Rose | Users, Roles |
Custom Home Page Examples
Add extra sections:
<RebaseStudio
homePage={
<StudioHomePage
sections={[
{
key: "analytics",
title: "Analytics",
children: <AnalyticsDashboard />,
},
]}
/>
}
/>
Add action buttons:
<RebaseStudio
homePage={
<StudioHomePage
additionalActions={
<Button onClick={exportAll}>Export All Data</Button>
}
/>
}
/>
Completely replace the home page:
<RebaseStudio
homePage={<MyCustomStudioHome />}
/>
Custom Login View
Use <RebaseAuth> with the loginView prop to replace the default login UI:
<Rebase client={rebaseClient} authController={authController}>
<RebaseAuth loginView={<MyCustomLoginPage />} />
<RebaseAdmin collections={collections} />
<RebaseStudio />
<RebaseShell title="My App" />
</Rebase>
The loginView is registered into the RebaseRegistry via registerAuth() and consumed by <RebaseAuthGate>.
Custom Views
Add custom React views to the Studio navigation using the AppView interface:
interface AppView {
slug: string;
name: string;
description?: string;
icon?: string | React.ReactNode;
hideFromNavigation?: boolean;
group?: string;
view: React.ReactNode;
nestedRoutes?: boolean;
roles?: string[];
}
Custom top-level views are added through the views prop on <RebaseAdmin>. They can also be contributed by plugins via plugin.views.
<RebaseAdmin
collections={collections}
views={[
{ slug: "dashboard", name: "Dashboard", icon: "LayoutDashboard", view: <Dashboard /> },
{ slug: "audit-log", name: "Audit Log", icon: "ScrollText", view: <AuditLog />, roles: ["admin"] },
]}
/>
<RebaseAdmin
collections={collections}
views={({ user, authController, data }) => [
{ slug: "dashboard", name: "Dashboard", icon: "LayoutDashboard", view: <Dashboard /> },
...(user?.roles?.includes("analyst")
? [{ slug: "reports", name: "Reports", icon: "FileText", view: <Reports /> }]
: []),
]}
/>
IMPORTANT FOR AGENTS: The roles field on AppView provides declarative role filtering — the view is excluded entirely (not just hidden from nav) if the user lacks a matching role. Use roles for simple access control and the builder function for dynamic/async cases. Both approaches compose.
CollectionPanel Component
CollectionPanel is a high-level wrapper for embedding collection views inside custom pages (dashboards, home pages, entity detail views):
import { CollectionPanel } from "@rebasepro/admin";
type CollectionPanelProps = {
path: string;
title?: string | false;
viewMode?: ViewMode;
sort?: [string, "asc" | "desc"];
limit?: number;
updateUrl?: boolean;
openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
className?: string;
collectionOverrides?: Partial<CollectionConfig>;
};
Usage Examples
import { CollectionPanel } from "@rebasepro/admin";
function MyDashboard() {
return (
<div>
{/* Simple usage */}
<CollectionPanel path="tasks" title="Pending Tasks" />
{/* With overrides */}
<CollectionPanel
path="clients"
viewMode="table"
limit={10}
sort={["createdAt", "desc"]}
collectionOverrides={{
defaultFilter: { status: ["!=", "completed"] }
}}
/>
{/* Hide title, custom open mode */}
<CollectionPanel
path="orders"
title={false}
openEntityMode="dialog"
/>
</div>
);
}
IMPORTANT FOR AGENTS: CollectionPanel defaults updateUrl to false so embedded panels don't hijack the browser URL. If you need URL sync, explicitly set updateUrl={true}.
Studio Bridge Hooks
The Studio Bridge provides CMS capabilities to Studio components. When CMS is present, real implementations are injected. When CMS is absent, noop defaults ensure Studio works standalone.
Bridge Interface
interface StudioBridge {
collectionRegistry: CollectionRegistryController;
sideEntityController: SidePanelController;
urlController: UrlController;
navigationState: NavigationStateController;
breadcrumbs: BreadcrumbsController;
}
Bridge Hook Reference
| Hook | Return Type | Description |
|---|
useStudioCollectionRegistry() | CollectionRegistryController | Access registered collections from Studio |
useStudioSidePanelController() | SidePanelController | Open/close entity side panels from Studio |
useStudioUrlController() | UrlController | Build URLs and navigate from Studio |
useStudioNavigationState() | NavigationStateController | Access navigation state from Studio |
useStudioBreadcrumbs() | BreadcrumbsController | Set breadcrumbs from Studio tools |
All bridge hooks are exported from @rebasepro/studio (re-exported from @rebasepro/app):
import {
useStudioCollectionRegistry,
useStudioSidePanelController,
useStudioUrlController,
useStudioNavigationState,
useStudioBreadcrumbs
} from "@rebasepro/studio";
BreadcrumbsController
interface BreadcrumbEntry {
title: string;
url: string;
count?: number | null;
id?: string;
}
interface BreadcrumbsController {
breadcrumbs: BreadcrumbEntry[];
set: (props: { breadcrumbs: BreadcrumbEntry[] }) => void;
updateCount: (id: string, count: number | null | undefined) => void;
}
StudioBridgeProvider (Advanced)
For custom wiring, use StudioBridgeProvider to inject CMS capabilities:
import { StudioBridgeProvider } from "@rebasepro/studio";
<StudioBridgeProvider value={{
collectionRegistry: useCollectionRegistryController(),
sidePanelController: useSidePanel(),
urlController: useUrlController(),
navigationState: useNavigationStateController(),
breadcrumbs: useBreadcrumbsController(),
}}>
<RebaseStudio />
</StudioBridgeProvider>
Core Hooks Reference
These hooks are exported from @rebasepro/app and available inside any <Rebase> provider tree:
Primary Hooks
| Hook | Return Type | Description |
|---|
useRebaseContext() | RebaseContext | Full Rebase context with all controllers |
useAuthController() | AuthController | Access current user and auth state |
useAdminModeController() | AdminModeController | Read/set admin mode (content / studio / settings) |
useEffectiveRoleController() | EffectiveRoleController | Simulate a role for previewing |
useModeController() | ModeController | Read/set color theme (light / dark) |
useSnackbarController() | SnackbarController | Show toast notifications |
useStorageSource() | StorageSource | Access file storage |
useData() | DataSource | Access the data source for CRUD ops |
useDialogsController() | DialogsController | Programmatic dialog management |
useCustomizationController() | CustomizationController | Access plugins, slots, property configs |
usePermissions() | { canCreate, canEdit, canDelete, canRead } | Role-aware permission checks |
UI & Layout Hooks
| Hook | Return Type | Description |
|---|
useLargeLayout() | boolean | true when viewport ≥ 1025px (lg breakpoint) |
useClipboard(options?) | useClipboardReturnType | Copy/cut to clipboard with ref or text |
useSlot(name, props) | ReactNode | null | Render plugin slot contributions |
useTranslation() | { t } | Access t() for i18n translations |
useCollapsedGroups(groups, namespace, defaults) | { isGroupCollapsed, toggleGroupCollapsed } | Manage collapsible navigation groups |
Navigation & Data Hooks (from @rebasepro/admin)
| Hook | Package | Description |
|---|
useSidePanel() | @rebasepro/admin | Open/close entity side panels |
useNavigationStateController() | @rebasepro/admin | Navigate between views |
useUrlController() | @rebasepro/admin | Build URLs and navigate |
useBreadcrumbsController() | @rebasepro/admin | Set breadcrumbs |
useRebaseRegistry() | @rebasepro/app | Access the full registry (CMS + Studio + Auth configs) |
useRebaseClient() | @rebasepro/app | Access the RebaseClient instance |
ModeController (Color Theme)
interface ModeController {
mode: "light" | "dark";
setMode: (mode: "light" | "dark" | "system") => void;
}
const { mode, setMode } = useModeController();
setMode("dark");
setMode("system");
useClipboard
interface UseClipboardProps {
onSuccess?: (text: string) => void;
onError?: (error: string) => void;
disableClipboardAPI?: boolean;
copiedDuration?: number;
}
const { copy, cut, isCoppied, clipboard, clearClipboard, ref, isSupported } = useClipboard({
copiedDuration: 2000
});
copy("Hello world");
<input ref={ref} />
<button onClick={() => copy()}>Copy</button>
usePermissions
const { canCreate, canEdit, canDelete, canRead } = usePermissions();
if (canCreate(myCollection, "products")) { ... }
if (canEdit(myCollection, "products", entity)) { ... }
useRebaseContext
Returns the full context object combining all controllers:
const context = useRebaseContext();
context.authController
context.data
context.storageSource
context.snackbarController
context.effectiveRoleController
context.databaseAdmin
context.client
RebaseShell Configuration
<RebaseShell> composes all CMS layers with sensible defaults:
interface RebaseShellProps {
title?: string;
appBar?: React.ReactNode;
drawer?: React.ReactNode;
autoOpenDrawer?: boolean;
children?: React.ReactNode;
}
Internally it composes:
<RebaseAuthGate>
<RebaseNavigation>
<RebaseRouteDefs layout={<RebaseLayout>}>
{children}
</RebaseRouteDefs>
</RebaseNavigation>
</RebaseAuthGate>
Visual Collection Editor
The Studio's collection editor allows non-developers to:
- Add, remove, and reorder fields
- Configure field types and validation
- Set up enum values and relations
- Preview the form layout
Under the hood, it uses AST manipulation (via ts-morph) to modify the TypeScript collection files — preserving all custom callbacks and code.
Enabling the Collection Editor
<RebaseAdmin collections={collections} collectionEditor={true} />
<RebaseAdmin
collections={collections}
collectionEditor={{
getAuthToken: authController.getAuthToken,
readOnly: false,
pathSuggestions: ["config/collections/"]
}}
/>
Virtual Collection Import
Collections are auto-loaded via a Vite plugin:
import { collections } from "virtual:rebase-collections";
This reads all collection files from the configured collections directory (e.g., config/collections/) and makes them available without manual barrel exports.
Running the Studio
pnpm run dev
This starts both frontend and backend. The Studio is accessible at http://localhost:5173 (Vite default).
Key Packages
| Package | Description |
|---|
@rebasepro/app | Core framework, hooks, types, <Rebase> provider |
@rebasepro/studio | Studio admin panel (<RebaseStudio>, StudioHomePage, bridge hooks) |
@rebasepro/admin | CMS frontend (<RebaseAdmin>, <RebaseShell>, CollectionPanel, collection editor) |
@rebasepro/ui | Component library (Tailwind v4 + Radix) |
@rebasepro/types | Shared TypeScript types |
@rebasepro/plugin-ai | AI-powered autofill |
@rebasepro/inference | Auto-infer schema from data |
References