Headless admin framework for Svelte 5 — bring your own backend
面向 Svelte 5 的 Headless 管理后台框架 — 自带后端适配
English | 中文 | 📖 Documentation / 文档
useList, useOne, useCreate, useUpdate, useDelete, useTable, useForm, useSelect, useInfiniteList, useShow, useStepsForm, useModalForm, useDrawerForm, etc.useLogin, useLogout, useRegister, useForgotPassword, useUpdatePassword, useGetIdentity, useIsAuthenticated, useOnError, usePermissions<Authenticated> — Conditionally render based on auth state with loading/fallbackKnownResources + auto type inference via InferData<R>unionFilters, unionSorters, file2Base64, getDefaultFilter, getDefaultSortOrder, generateDefaultDocumentTitleuseExport / useImport for CSV export/import with batch supportuseList、useOne、useCreate、useUpdate、useDelete、useTable、useForm、useSelect、useInfiniteList、useShow、useStepsForm、useModalForm、useDrawerForm 等useLogin、useLogout、useRegister、useForgotPassword、useUpdatePassword、useGetIdentity、useIsAuthenticated、useOnError、usePermissions<Authenticated> — 根据认证状态条件渲染,支持 loading/fallbackKnownResources)+ 自动类型推断(InferData<R>)unionFilters、unionSorters、file2Base64、getDefaultFilter、getDefaultSortOrder、generateDefaultDocumentTitleuseExport / useImport,支持 CSV 批量导入导出| Package | Description / 描述 |
|---|---|
@svadmin/core |
Hooks, providers, types, utilities, Resource Type Registry |
@svadmin/ui |
Pre-built admin components / 预构建管理组件(shadcn-svelte) |
@svadmin/create |
CLI scaffolding tool / CLI 脚手架工具 |
@svadmin/refine-adapter |
Bridge any @refinedev/* data provider to svadmin / 桥接 Refine 生态数据源 |
@svadmin/sveltekit |
SvelteKit router integration / SvelteKit 路由集成 |
@svadmin/lite |
SSR-only variant (no client JS) / 纯 SSR 变体 |
@svadmin/sso |
OIDC/OAuth2 SSO plugin / 单点登录插件 |
@svadmin/editor |
Rich-text editor component / 富文本编辑器组件 |
@svadmin/mcp |
MCP (Model Context Protocol) integration / AI 工具集成 |
| Package | Backend |
|---|---|
@svadmin/simple-rest |
REST API (zero deps, JWT/Cookie auth) |
@svadmin/supabase |
Supabase (data + auth + live) |
@svadmin/drizzle |
Drizzle ORM (SQLite, PostgreSQL, MySQL, D1) |
@svadmin/pocketbase |
PocketBase (data + auth + live) |
@svadmin/appwrite |
Appwrite (data + auth + live) |
@svadmin/graphql |
GraphQL |
@svadmin/elysia |
Elysia (auto type inference via Eden Treaty) |
@svadmin/strapi |
Strapi CMS |
@svadmin/directus |
Directus |
@svadmin/firebase |
Firebase / Firestore |
@svadmin/hasura |
Hasura GraphQL |
@svadmin/sanity |
Sanity.io |
@svadmin/airtable |
Airtable |
@svadmin/medusa |
Medusa Commerce |
@svadmin/nestjs-query |
NestJS GraphQL |
@svadmin/nestjsx-crud |
NestJS CRUD |
# Install
bun add @svadmin/core @svadmin/ui @svadmin/simple-rest
<script lang="ts">
import { AdminApp } from '@svadmin/ui';
import { createSimpleRestDataProvider } from '@svadmin/simple-rest';
import { resources } from './resources';
const dataProvider = createSimpleRestDataProvider({
apiUrl: 'https://jsonplaceholder.typicode.com',
});
</script>
<AdminApp {dataProvider} {resources} title="My Admin" defaultTheme="system" />
import type { ResourceDefinition } from "@svadmin/core";
export const resources: ResourceDefinition[] = [
{
name: "products",
label: "Products",
fields: [
{ key: "id", label: "ID", type: "number", showInForm: false },
{
key: "name",
label: "Name",
type: "text",
required: true,
searchable: true,
},
{ key: "price", label: "Price", type: "number", required: true },
{
key: "status",
label: "Status",
type: "select",
options: [
{ label: "Active", value: "active" },
{ label: "Draft", value: "draft" },
],
},
],
},
];
<script lang="ts">
import { AdminApp } from '@svadmin/ui';
import { createSupabaseDataProvider, createSupabaseAuthProvider } from '@svadmin/supabase';
import { createClient } from '@supabase/supabase-js';
import { resources } from './resources';
import Login from './pages/Login.svelte';
const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY
);
</script>
<AdminApp
dataProvider={createSupabaseDataProvider(supabase)}
authProvider={createSupabaseAuthProvider(supabase)}
{resources}
title="My App"
>
{#snippet loginPage()}<Login />{/snippet}
</AdminApp>
// Server: export your Elysia app type
import { Elysia } from "elysia";
const app = new Elysia().get("/posts", () => db.posts.findMany());
export type App = typeof app;
// Client: auto-infer resource types
import { createElysiaDataProvider } from "@svadmin/elysia";
import type { InferResourceMap } from "@svadmin/elysia";
import type { App } from "./server";
// ResourceTypeMap auto-derived from Elysia routes
declare module "@svadmin/core" {
interface ResourceTypeMap extends InferResourceMap<App> {}
}
const dataProvider = createElysiaDataProvider<App>("http://localhost:3000");
Important / 重要: Tailwind CSS v4 does not scan
node_modulesby default. You must add@sourcedirectives so that utility classes used by@svadmin/uicomponents are generated.Tailwind CSS v4 默认不扫描
node_modules。你必须添加@source指令,否则@svadmin/ui组件使用的工具类不会被生成,导致布局完全错乱。
1. Add @source to your CSS entry file / 在 CSS 入口文件中添加 @source:
/* app.css */
@import "tailwindcss";
/* Required: tell Tailwind v4 to scan svadmin component sources */
@source "../node_modules/@svadmin/ui/src";
@source "../node_modules/@svadmin/core/src";
2. Configure Vite optimizeDeps / 配置 Vite optimizeDeps:
Since @svadmin/ui ships raw .svelte source files (not pre-built), exclude the svadmin packages from pre-bundling and explicitly include their CJS peer dependencies. If you use @svadmin/supabase in a Vite app, make sure it is also listed in optimizeDeps.exclude, otherwise Vite may pre-bundle it and throw dev-time optional-peer/export errors such as createRefineAdapter not being found:
由于 @svadmin/ui 提供的是原始 .svelte 源码文件(非预构建),需要将 svadmin 包排除在预打包之外,并显式包含其 CJS 对等依赖。如果你的 Vite 项目使用了 @svadmin/supabase,也必须把它加入 optimizeDeps.exclude;否则 Vite 可能错误预打包它,并在开发环境里抛出 createRefineAdapter 找不到之类的 optional-peer/export 异常:
// vite.config.ts
export default defineConfig({
optimizeDeps: {
exclude: ["@svadmin/core", "@svadmin/ui", "@svadmin/supabase"],
include: [
"@svadmin/core > @tanstack/svelte-query",
"@svadmin/ui > svelte-sonner",
"@svadmin/ui > vaul-svelte",
"@svadmin/ui > cmdk-sv",
"@svadmin/ui > bits-ui",
"@svadmin/ui > @tanstack/svelte-table",
"@svadmin/ui > @lucide/svelte",
"highlight.js",
"marked",
"marked-highlight",
"isomorphic-dompurify",
],
},
});
<AdminApp> Props| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
dataProvider |
DataProvider |
✅ | — | Data source adapter / 数据源适配器 |
authProvider |
AuthProvider |
— | — | Auth adapter / 认证适配器 |
routerProvider |
RouterProvider |
— | hash | Custom router / 自定义路由提供者 |
resources |
ResourceDefinition[] |
✅ | — | Resource definitions / 资源定义 |
title |
string |
— | 'Admin' |
App title / 应用标题 |
defaultTheme |
'light' | 'dark' | 'system' |
— | 'system' |
Initial theme / 初始主题 |
themeConfig |
ThemeConfig |
— | — | Theme config (strategy, overrides) / 主题配置 |
locale |
string |
— | auto | Override locale / 覆盖语言 |
dashboard |
Snippet |
— | — | Custom dashboard / 自定义仪表盘 |
loginPage |
Snippet |
— | — | Custom login page / 自定义登录页 |
components |
Partial<ComponentRegistry> |
— | — | Override default UI components / 覆盖默认组件 |
Dark mode works out of the box. Use defaultTheme prop or the Sidebar toggle:
暗色模式开箱即用。使用 defaultTheme prop 或侧边栏切换按钮:
<!-- Follow system / 跟随系统 -->
<AdminApp {dataProvider} {resources} defaultTheme="system" />
<!-- Always dark / 始终暗色 -->
<AdminApp {dataProvider} {resources} defaultTheme="dark" />
<!-- Dark-first strategy for dark-themed apps / 默认暗色的应用 -->
<AdminApp {dataProvider} {resources} themeConfig={{ strategy: 'dark-first' }} />
Programmatic control / 编程式控制:
import {
setTheme,
toggleTheme,
getTheme,
getResolvedTheme,
} from "@svadmin/core";
setTheme("dark"); // 'light' | 'dark' | 'system'
toggleTheme(); // toggle between light/dark
getTheme(); // current setting
getResolvedTheme(); // resolved to 'light' or 'dark'
Switch between 6 color palettes via sidebar picker or programmatically:
通过侧边栏选色器或编程式切换 6 种配色:
import { getColorTheme, setColorTheme, colorThemes } from "@svadmin/core";
import type { ColorTheme } from "@svadmin/core";
setColorTheme("rose"); // 'blue' | 'green' | 'rose' | 'orange' | 'violet' | 'zinc'
getColorTheme(); // current color theme
console.log(colorThemes); // [{ id: 'blue', label: 'Blue', color: '#3b82f6' }, ...]
Available themes / 可用主题: blue (default), green, rose, orange, violet, zinc
For end-to-end type safety, register your resource types:
注册资源类型以获得端到端类型安全:
// Extend the ResourceTypeMap interface (declaration merging)
declare module "@svadmin/core" {
interface ResourceTypeMap {
posts: { id: number; title: string; body: string };
users: { id: number; name: string; email: string };
}
}
// Now all hooks get compile-time checking:
useList({ resource: "posts" }); // ✅ OK
useList({ resource: "postz" }); // ❌ Compile error — typo caught!
Implement the DataProvider interface to connect any backend:
实现 DataProvider 接口即可接入任意后端:
import type { DataProvider, BaseRecord } from "@svadmin/core";
const myProvider: DataProvider = {
getApiUrl: () => "https://api.example.com",
getList: async ({ resource, pagination, sorters, filters }) => {
/* ... */
},
getOne: async ({ resource, id }) => {
/* ... */
},
create: async ({ resource, variables }) => {
/* ... */
},
update: async ({ resource, id, variables }) => {
/* ... */
},
deleteOne: async ({ resource, id }) => {
/* ... */
},
};
┌──────────────────────────────────────────┐
│ Your App / 你的应用 │
│ (resources, pages, providers) │
├──────────────────────────────────────────┤
│ @svadmin/ui │
│ AdminApp · AutoTable · AutoForm │
│ ShowPage · 16 Fields · shadcn-svelte │
├──────────────────────────────────────────┤
│ @svadmin/core │
│ 30+ Hooks · Context · Router · i18n │
│ Permissions · Audit · Theme │
│ Resource Type Registry · Helpers │
├──────────┬───────────┬───────────────────┤
│ /supabase│ /drizzle │ /simple-rest │
│ /appwrite│ /graphql │ /elysia │
│ /firebase│ /hasura │ /pocketbase ... │
├──────────┴───────────┴───────────────────┤
│ @svadmin/refine-adapter │
│ Bridge any @refinedev/* provider │
└──────────────────────────────────────────┘
Full documentation is available at / 完整文档请访问:
Contributions are welcome! 欢迎贡献!
git checkout -b feat/amazing-feature)git commit -m 'feat: add amazing feature')git push origin feat/amazing-feature)MIT