A config-driven CMS table editor for managing Webflow collections with SvelteKit.
bun add svelte-webflow-cms
# or
npm install svelte-webflow-cms
This library uses Tailwind CSS classes for styling. You must configure Tailwind to scan this package's files so the necessary CSS is generated.
Add a @source directive in your CSS file:
@import "tailwindcss";
@source "../node_modules/svelte-webflow-cms";
// src/routes/members/config.ts
import type { TableConfig } from "svelte-webflow-cms";
export const config: TableConfig = {
pageTitle: "Team Members",
itemSingular: "Member",
itemPlural: "Members",
siteId: "your-site-id",
collectionId: "your-collection-id",
createDeleteEnabled: true,
draftEnabled: true,
fields: [
{
visible: true,
editable: true,
required: true,
schema: {
name: "Name",
slug: "name",
type: "PlainText",
validations: { maxLength: 100 },
},
},
{
visible: true,
editable: true,
schema: {
name: "Photo",
slug: "photo",
type: "Image",
imageSettings: { width: 400, height: 400 },
},
},
],
};
// src/routes/members/+page.server.ts
import { createCmsActions, loadCmsItems } from "svelte-webflow-cms/server";
import { createR2UploadProvider } from "svelte-webflow-cms/providers/r2";
import { config } from "./config";
export async function load({ platform }) {
const token = platform?.env?.WEBFLOW_TOKEN;
if (!token) return { items: [], error: "Token not found" };
const { items, error } = await loadCmsItems(token, config);
return { items, error };
}
export const actions = createCmsActions(config, {
getToken: (_, platform) => platform?.env?.WEBFLOW_TOKEN ?? null,
getUploadProvider: (_, platform) =>
platform?.env?.TEMP_IMAGES
? createR2UploadProvider(
platform.env.TEMP_IMAGES,
"https://cdn.example.com"
)
: null,
bucketPrefix: "members",
});
<!-- src/routes/members/+page.svelte -->
<script lang="ts">
import { CmsTable } from 'svelte-webflow-cms';
import { config } from './config';
let { data } = $props();
</script>
<CmsTable.Root {config} data={data.items}>
<CmsTable.Toolbar />
<CmsTable.SaveBar />
<CmsTable.Table>
<CmsTable.Header />
<CmsTable.Body />
</CmsTable.Table>
</CmsTable.Root>
The CmsTable components follow a composable pattern (similar to shadcn-svelte), allowing you to customize and extend the table easily.
<script>
import { CmsTable } from 'svelte-webflow-cms';
</script>
<CmsTable.Root {config} data={data.items} referenceData={data.referenceData}>
<CmsTable.Toolbar />
<CmsTable.SaveBar />
<CmsTable.Table>
<CmsTable.Header />
<CmsTable.Body />
</CmsTable.Table>
</CmsTable.Root>
| Component | Description |
|---|---|
CmsTable.Root |
Root wrapper - provides context and state |
CmsTable.Toolbar |
Title and add button |
CmsTable.SaveBar |
Save/cancel controls with validation display |
CmsTable.Table |
Table container |
CmsTable.Header |
Table header row with field names |
CmsTable.Body |
Table body with drag-and-drop support |
CmsTable.Row |
Individual row (used in custom row templates) |
CmsTable.Actions |
Actions column (live toggle and delete button) |
CmsTable.Cell |
Field cell with input (used in custom rows) |
<CmsTable.Root {config} data={data.items}>
<CmsTable.Toolbar>
{#snippet afterTitle()}
<Badge variant="secondary">{data.items.length} items</Badge>
{/snippet}
</CmsTable.Toolbar>
<CmsTable.SaveBar />
<CmsTable.Table>
<CmsTable.Header />
<CmsTable.Body />
</CmsTable.Table>
</CmsTable.Root>
<CmsTable.Root {config} data={data.items}>
<div class="flex items-center justify-between">
<CmsTable.Toolbar showAddButton={false} />
<div class="flex gap-2">
<Button onclick={handleExport}>Export</Button>
<CmsTable.SaveBar />
</div>
</div>
<CmsTable.Table>
<CmsTable.Header />
<CmsTable.Body />
</CmsTable.Table>
</CmsTable.Root>
<CmsTable.Table>
<CmsTable.Header>
{#snippet afterColumns()}
<Table.Head>Custom Column</Table.Head>
{/snippet}
</CmsTable.Header>
<CmsTable.Body />
</CmsTable.Table>
<CmsTable.Body>
{#snippet row({ item, index, isNew })}
<CmsTable.Row {item} {index} {isNew}>
{#snippet afterColumns()}
<Table.Cell>
<Badge>{item.status}</Badge>
</Table.Cell>
{/snippet}
</CmsTable.Row>
{/snippet}
</CmsTable.Body>
All components accept a class prop for custom styling:
<CmsTable.Root {config} data={data.items} class="max-w-7xl mx-auto">
<CmsTable.Toolbar class="bg-gray-50 p-4 rounded-t-lg" />
<CmsTable.SaveBar class="justify-start px-4" />
<CmsTable.Table class="border-2 shadow-lg">
<CmsTable.Header class="bg-blue-50" />
<CmsTable.Body class="text-sm" />
</CmsTable.Table>
</CmsTable.Root>
The library supports pluggable storage backends. Implement the UploadProvider interface for your storage:
interface UploadProvider {
upload(
file: Blob,
filename: string,
contentType: string
): Promise<{ url: string; filename: string }>;
delete(filename: string): Promise<void>;
}
import { createR2UploadProvider } from "svelte-webflow-cms/providers/r2";
getUploadProvider: (_, platform) =>
createR2UploadProvider(platform.env.BUCKET, "https://cdn.example.com");
export function createS3UploadProvider(
client,
bucket,
baseUrl
): UploadProvider {
return {
async upload(file, filename, contentType) {
await client.send(
new PutObjectCommand({
Bucket: bucket,
Key: filename,
Body: Buffer.from(await file.arrayBuffer()),
ContentType: contentType,
})
);
return { url: `${baseUrl}/${filename}`, filename };
},
async delete(filename) {
await client.send(
new DeleteObjectCommand({ Bucket: bucket, Key: filename })
);
},
};
}
The getToken function receives the request and platform at runtime:
// Cloudflare Pages
getToken: (_, platform) => platform?.env?.WEBFLOW_TOKEN ?? null;
// Node.js
getToken: () => process.env.WEBFLOW_TOKEN ?? null;
// SvelteKit $env
import { env } from "$env/dynamic/private";
getToken: () => env.WEBFLOW_TOKEN ?? null;
| Type | Input Component | Notes |
|---|---|---|
PlainText |
TextInput | Supports maxLength/minLength validation |
RichText |
TextInput | Supports maxLength/minLength validation |
Link |
LinkInput | URL input |
Email |
EmailInput | Email input |
Phone |
PhoneInput | Phone input |
Number |
NumberInput | Numeric input with range validation |
Switch |
SwitchInput | Boolean toggle |
Option |
OptionInput | Dropdown select |
Color |
ColorInput | Color picker |
DateTime |
DateInput | Calendar picker with date selection |
Image |
ImageInput | Image upload with processing |
Reference |
ReferenceInput | Single collection reference |
MultiReference |
MultiReferenceInput | Multiple collection refs |
interface Field {
visible: boolean; // Show in table
editable?: boolean; // Allow editing
required?: boolean; // Field is required
styles?: FieldStyles; // Custom styling
schema: FieldSchema; // Field schema
}
interface Validations {
minLength?: number; // Minimum string length
maxLength?: number; // Maximum string length
min?: number; // Minimum numeric value
max?: number; // Maximum numeric value
}
Sort fields now support DateTime type in addition to Number:
interface SortField extends Field {
direction?: "asc" | "desc"; // Sort direction
schema: SortFieldSchema;
}
interface SortFieldSchema extends FieldSchema {
type: "Number" | "DateTime"; // Number or DateTime
}
CmsTable Components:
CmsTable.Root - Root wrapper that provides context and state managementCmsTable.Toolbar - Title and add button with customizable slotsCmsTable.SaveBar - Save/cancel controls with validation error displayCmsTable.Table - Table container with stylingCmsTable.Header - Table header row with field names and tooltipsCmsTable.Body - Table body with drag-and-drop supportCmsTable.Row - Individual row component (for custom row templates)CmsTable.Actions - Actions column with live toggle and delete buttonCmsTable.Cell - Field cell with appropriate input componentInput Components (can be used independently):
TextInput - Plain text and rich text inputNumberInput - Numeric input with validationLinkInput - URL inputEmailInput - Email inputPhoneInput - Phone inputColorInput - Color pickerSwitchInput - Boolean toggleOptionInput - Dropdown selectDateInput - Calendar date pickerImageInput - Image upload with compressionReferenceInput - Single collection reference selectorMultiReferenceInput - Multiple collection reference selectorcreateCmsActions(config, options) - Create all CMS actionsloadCmsItems(token, config) - Load items from WebflowloadReferenceData(token, config) - Load referenced collection datacreateWebflowClient(token) - Create Webflow API clientTableConfig - Table configurationField - Field configurationUploadProvider - Upload provider interfaceUploadProviderFactory - Factory for creating providersTokenGetter - Token retrieval function typeMIT