A config-driven CMS table editor for managing Webflow collections with SvelteKit
npm install svelte-webflow-cmsA config-driven CMS table editor for managing Webflow collections with SvelteKit.
- Config-driven: Define your table structure with a simple configuration object
- Change Tracking: Tracks all modifications and only enables save when changes exist
- Batched Operations: Nothing is sent to Webflow until explicit "Save changes" click
- Drag & Drop Sorting: Reorder items with automatic sort field updates
- Direct Webflow Upload: Images and files upload directly to Webflow assets (no intermediate storage needed)
- Hosting Agnostic: Works with any hosting platform (Cloudflare, Vercel, Netlify, etc.)
- Field Validation: Required fields, length constraints, and numeric ranges with error feedback
``bash`
bun add svelte-webflow-cmsor
npm install svelte-webflow-cms
- SvelteKit 2.x
- Svelte 5.x
- Tailwind CSS v4
- bits-ui ^2.0.0
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:
`css`
@import "tailwindcss";
@source "../node_modules/svelte-webflow-cms";
`typescript
// 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
},
},
],
};
`
`typescript
// 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,
});
`
`svelte
`
The CmsTable components follow a composable pattern (similar to shadcn-svelte), allowing you to customize and extend the table easily.
`svelte
`
| 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) |
#### Custom Toolbar with Badge
`svelte`
{#snippet afterTitle()}
{/snippet}
#### Custom Actions Placement
`svelte`
#### Custom Column in Header
`svelte`
{#snippet afterColumns()}
{/snippet}
#### Custom Row Template
`svelte`
{#snippet row({ item, index, isNew })}
{#snippet afterColumns()}
{/snippet}
{/snippet}
#### Styling with Classes
All components accept a class prop for custom styling:
`svelte`
Images and files are uploaded directly to Webflow's asset storage using their Assets API. No intermediate storage (R2, S3, etc.) is required.
1. Images: Client-side compression and cropping based on imageSettings, then uploaded to Webflow
2. Files: Validated for type and size, then uploaded to Webflow on save
3. The returned Webflow asset URL is used in the CMS item
`typescript`
{
schema: {
name: "Photo",
slug: "photo",
type: "Image",
imageSettings: {
width: 400, // Target width in pixels
height: 400, // Target height in pixels
},
},
}
`typescript`
{
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:
`typescript`
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:
`typescript
// 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 |
`typescript`
interface Field {
visible: boolean; // Show in table
editable?: boolean; // Allow editing
required?: boolean; // Field is required
styles?: FieldStyles; // Custom styling
schema: FieldSchema; // Field schema
}
`typescript`
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:
`typescript
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 component
Input 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 selector
- createCmsActions(config, options) - Create all CMS actionsloadCmsItems(token, config)
- - Load items from WebflowloadReferenceData(token, config)
- - Load referenced collection datacreateWebflowClient(token)
- - Create Webflow API client
- TableConfig - Table configurationField
- - Field configurationImageSettings
- - Image upload settings (width, height)FileSettings
- - File upload settings (maxSizeBytes)TokenGetter` - Token retrieval function type
-
MIT