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",
assetFolderId: "optional-folder-id", // Optional: organize uploads in a Webflow folder
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 },
},
},
{
visible: true,
editable: true,
schema: {
name: "Resume",
slug: "resume",
type: "File",
fileSettings: { maxSizeBytes: 10 * 1024 * 1024 }, // 10MB max
},
},
],
};
// src/routes/members/+page.server.ts
import { createCmsActions, loadCmsItems } from "svelte-webflow-cms/server";
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,
});
<!-- 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>
Images and files are uploaded directly to Webflow's asset storage using their Assets API. No intermediate storage (R2, S3, etc.) is required.
imageSettings, then uploaded to Webflow{
schema: {
name: "Photo",
slug: "photo",
type: "Image",
imageSettings: {
width: 400, // Target width in pixels
height: 400, // Target height in pixels
},
},
}
{
schema: {
name: "Document",
slug: "document",
type: "File",
fileSettings: {
maxSizeBytes: 10 * 1024 * 1024, // 10MB max file size
},
},
}
The following file types are supported for file uploads:
| Category | Extensions |
|---|---|
| Images | PNG, JPEG/JPG, GIF, BMP, SVG, WebP |
| Documents | PDF, DOC/DOCX, TXT |
| Spreadsheets | XLS/XLSX, CSV, ODS |
| Presentations | PPT/PPTX, ODP |
| Other | ODT |
Optionally specify an assetFolderId in your config to organize uploads:
const config: TableConfig = {
siteId: "your-site-id",
collectionId: "your-collection-id",
assetFolderId: "your-folder-id", // Optional
// ...
};
To find your folder ID, use the Webflow API or check the URL when viewing a folder in the Webflow dashboard.
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 |
File |
FileInput | File upload with type/size validation |
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 compressionFileInput - File upload with type/size validationReferenceInput - 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 configurationImageSettings - Image upload settings (width, height)FileSettings - File upload settings (maxSizeBytes)TokenGetter - Token retrieval function typeMIT