fields Component
Documentation for the dynamic field renderer with widget loading, validation store integration, and revision history.
On this page
The fields component is responsible for rendering collection fields dynamically, loading appropriate widgets, handling validation, and managing revision history.
Collection Entry Editing Ecosystem
The fields component is part of a larger system for editing collection entries. The following components work together:
Mobile] RS[right-sidebar
Desktop] end subgraph Content["Content Editing"] F[fields Component] WL[widget-loader] W[Widgets] end subgraph Support["Supporting Components"] TS[translation-status] TP[TokenPicker] REV[Revisions] end subgraph Stores["State Stores"] VS[validationStore] CV[collectionValue] DC[dataChangeStore] TRP[translationProgress] end HE --> VS RS --> VS F --> WL WL --> W W --> CV W --> VS F --> REV TS --> TRP TP --> W CV --> DC
Component Roles
| Component | Path | Role |
|---|---|---|
| header-edit | src/components/header-edit.svelte |
Mobile save/cancel/delete actions, validation gating |
| right-sidebar | src/components/right-sidebar.svelte |
Desktop save/status/scheduling actions |
| fields | src/components/collection-display/fields.svelte |
Dynamic field rendering, validation |
| widget-loader | src/components/collection-display/widget-loader.svelte |
Async widget loading with error handling |
| translation-status | src/components/collection-display/translation-status.svelte |
Translation progress per language |
| TokenPicker | src/components/TokenPicker.svelte |
Dynamic token insertion for fields |
| Revisions | (within fields) | Historical snapshots with revert capability |
👥 Collaborative Editing (Phase 2 - Yjs Integration)
SveltyCMS implements real-time collaborative editing using Yjs CRDTs. This ensures that multiple users can edit the same entry simultaneously without data loss or conflicts.
Collaborative Architecture
The collaborative system uses a Single Source of Truth (SSoT) model during the editing session, synchronized via a WebSocket provider (e.g., Hocuspocus).
flowchart TB
subgraph Clients["Concurrent Editors"]
C1[User A]
C2[User B]
end
subgraph Sync["Synchronization Layer"]
P[Yjs Provider<br/>WebSocket/Hocuspocus]
YD[Y.Doc<br/>Shared Document State]
AW[Awareness<br/>Cursors/Presence]
end
subgraph CMS["SveltyCMS Core"]
F[fields Component]
CV[collectionValue Store]
DB[(Database)]
end
C1 <--> P
C2 <--> P
P <--> YD
YD <--> AW
YD --"Reconciles"--> F
F --"Updates"--> CV
CV --"Saves (Debounced)"--> DB
Key Components of Collaboration
| Feature | Implementation | Description |
|---|---|---|
| Shared State | Y.Doc |
A Conflict-free Replicated Data Type (CRDT) document that holds all entry fields. |
| Presence | Awareness |
Tracks remote cursors, active users, and field-level focus highlights. |
| Sync Provider | Hocuspocus |
The recommended enterprise WebSocket provider for synchronizing Yjs docs with the server. |
| Widget Binding | y-svelte |
Widgets bind their inputs directly to the Y.Doc sub-types (e.g., Y.Text for inputs, Y.Xml for RichText). |
Data Flow for Real-time Sync
- Initialization: When an entry is opened,
fields.svelteinitializes aY.Docand connects to the provider. - Local Change: A user types in a widget. The widget updates its corresponding sub-type in the
Y.Doc. - Propagation: Yjs automatically propagates the delta to all other connected clients and the server.
- Reconciliation:
fields.sveltewatches theY.Docfor changes and updates the globalcollectionValuestore to maintain compatibility with non-collaborative parts of the system (like Save actions). - Awareness: Remote cursors are rendered using absolute positioning relative to the field containers, providing visual feedback of where others are working.
Architecture
flowchart TB
subgraph fields["fields Component"]
direction TB
FD[Field Definitions]
WL[Widget Loader]
VL[Validation Logic]
RH[Revision History]
end
subgraph Widgets["Widget System"]
TW[Text Widget]
RTW[RichText Widget]
MW[Media Widget]
RW[Relation Widget]
CW[Custom Widgets]
end
subgraph Stores["State Stores"]
CV[collectionValue]
VS[validationStore]
TP[translationProgress]
DC[dataChangeStore]
end
FD --> WL
WL --> Widgets
Widgets --> CV
CV --> VL
VL --> VS
VS --> DC
RH --> CV
Features
| Feature | Description |
|---|---|
| Dynamic Widget Loading | Lazy-loads widgets based on field type using widget-loader |
| Validation Store Integration | Real-time field validation with validationStore |
| Translation Progress | Visual indicators for multilingual content completion |
| Revision History | View and revert to previous versions |
| Role-Based Filtering | Show/hide fields based on user permissions |
| Two-Way Binding | Sync between local and global state |
header-edit & right-sidebar (Save Actions)
The save functionality is split between mobile and desktop views:
header-edit (Mobile)
Visible on screens < 1024px. Provides:
- Save Button: Disabled when
validationStore.isValid === false - Cancel/Delete: With confirmation modals
- Clone: Duplicate entry functionality
- Scheduling: Publish/unpublish scheduling
// Save validation gating
const canSave = $derived(validationStore.isValid && dataChangeStore.hasChanges);
right-sidebar (Desktop)
Visible on screens >= 1024px. Additionally provides:
- Entry Status: Draft/Published/Scheduled
- Metadata Display: Created/Updated timestamps
- Scheduling Panel: Date/time pickers for publish schedule
Both components integrate with validationStore to prevent saving invalid entries.
translation-status
Displays translation completion progress for multilingual collections:
flowchart LR
F[fields] --> TS[translation-status]
TS --> |tracks| TP[translationProgress]
TP[translationProgress]
TS --> |displays| Languages
Languages --> Progress[% Complete]
Features {#features-2}
- Per-Language Progress: Shows completion % for each language
- Field-Level Tracking: Tracks individual translatable fields
- Widget-Aware: Handles complex widgets (e.g., SEO) with nested language data
- Language Switcher: Click to switch content language
Progress Calculation
// Translation progress per language
translationProgress[language] = {
total: new SvelteSet<string>(), // All translatable fields
translated: new SvelteSet<string>(), // Completed translations
};
// Progress percentage
const progress = (translated.size / total.size) * 100;
TokenPicker
A floating panel for inserting dynamic tokens into input fields:
Features {#features-3}
- Token Categories: Entry, User, Site, System tokens
- Modifier Support: date, upper, lower, truncate, etc.
- Live Preview: Shows resolved token value
- Smart Detection: Detects existing tokens in active input
- Draggable Window: Repositionable UI
Usage
Tokens are inserted with {{ }} syntax:
{
{
entry: title;
}
} // Simple token
{
{
entry: date | date(short);
}
} // With modifier
{
{
user: displayName | upper;
}
} // Uppercase modifier
Validation Integration
The fields component integrates with the global validationStore for real-time validation feedback.
Validation Flow
sequenceDiagram
participant F as fields
participant W as Widget
participant VS as validationStore
participant UI as Save Button
F->>W: Render with field config
W->>W: User input
W->>VS: setError(fieldName, message)
VS->>UI: isValid = false
Note over UI: Save disabled
W->>W: User fixes input
W->>VS: clearError(fieldName)
VS->>UI: isValid = true
Note over UI: Save enabled
Required Field Validation
The component automatically validates required fields:
$effect(() => {
const values = currentCollectionValue;
filteredfields.forEach((field) => {
if (field.required) {
const fieldName = getFieldName(field, false);
const value = values[fieldName];
const isEmpty =
value === null ||
value === undefined ||
(typeof value === "string" && value.trim() === "") ||
(Array.isArray(value) && value.length === 0);
if (isEmpty) {
validationStore.setError(fieldName, `${field.label || fieldName} is required`);
} else {
validationStore.clearError(fieldName);
}
}
});
});
Widget-Level Validation
Individual widgets can implement their own validation using Valibot schemas:
// In widget input.svelte
import { validationStore } from "@stores/store.svelte";
import { parse } from "valibot";
function validateInput() {
try {
parse(validationSchema, currentValue);
validationStore.clearError(fieldName);
} catch (error) {
const message = error.issues?.[0]?.message || "Invalid input";
validationStore.setError(fieldName, message);
}
}
Widget Loading Flow
sequenceDiagram
participant F as fields
participant WL as widget-loader
participant CL as ComponentLoader (import)
participant W as Widget Component
F->>WL: Render field
WL->>CL: Execute dynamic import()
CL-->>WL: Resolve Svelte Component
WL->>W: Mount with props
W->>F: bind:value updates
widget-loader Error Handling
The widget-loader component handles loading failures gracefully:
- Loading State: Shows Native UI while widget loads
- Error State: Displays error with retry button
- Fallback State: Shows warning for unavailable widgets
Props
interface fieldsProps {
fields: FieldDefinition[];
collection: Collection;
entry: Entry;
entryId: string;
collectionValue: ValueStore;
revisions?: Revision[];
contentLanguage?: string;
}
Tabs
The component uses a tabbed interface with up to 4 tabs (conditional based on settings):
Tab 0: Edit
The primary editing interface with all field widgets.
Tab 1: Revisions
Only visible if collection.revision is enabled. Shows historical snapshots with:
- Version timeline
- Compare functionality
- Revert capability
Tab 2+: Live Preview (Editable Website plugin)
When the Editable Website & Live Preview plugin is enabled on a collection (plugins: ["editable-website"] and livePreview configured), a Live Preview tab appears via the entry_edit injection zone.
Requires a marketplace license (€14.99) or active 14-day trial.
Features:
- Signed preview URL via
POST /api/preview/authorize - Bidirectional sync — CMS edits push to iframe; preview can send
svelty:saveback to the form - Click-to-edit — click
data-svelty-fieldelements in the preview to focus CMS fields - Device preview — desktop / tablet / mobile widths
- Copy URL and open in new tab
Works with the optional SvelteKit Site Starter (routes/(site)) or any external frontend implementing the protocol.
For handshake security, postMessage types, and external frontend setup, see Live Preview Architecture.
Tab 3: API (Admin Only)
Only visible for admin users. Shows:
- API URL for the current entry:
/api/collection/{collectionId}/{entryId} - Copy button for the URL
- Raw JSON view of the entry data
Plugin Extensibility
The fields component supports plugin extensions through the entry_edit injection zone, allowing plugins to add custom tabs and functionality to the entry editing interface.
Plugin Slots
Plugins can register components in the entry_edit zone to:
- Add Custom Tabs: SEO optimization, AI content generation, preview tools
- Inject UI Elements: Custom actions, metadata panels, workflow controls
- Extend Functionality: Third-party integrations, analytics, custom validators
Example Plugin Integration
// Plugin registering a custom SEO tab
import type { Plugin } from "@src/plugins/types";
import SEOTab from "./SEOTab.svelte";
export const seoPlugin: Plugin = {
metadata: {
id: "seo-optimizer",
name: "SEO Optimizer",
version: "1.0.0",
enabled: true,
},
ui: {
slots: [
{
zone: "entry_edit",
slot: "tabs",
component: SEOTab,
order: 100,
},
],
},
};
Available Slots in entry_edit Zone
| Slot Name | Location | Use Case |
|---|---|---|
tabs |
Additional tabs after Edit/Revisions/Preview | Custom editing interfaces |
header_actions |
Header toolbar area | Quick actions, status controls |
field_extensions |
Below field groups | Field-level enhancements |
For complete plugin development guide, see Plugin Architecture.
Best Practices
- Field Configuration: Always set
db_fieldNamefor consistent validation keys - Required fields: Use
required: truefor mandatory fields (handled automatically) - Custom Validation: Implement Valibot schemas in widget
index.tsfor type-specific validation - Error Messages: Provide clear, user-friendly validation messages
- Permissions: Use
permissionsfield config to control access per role - Token Support: Use
TokenPickerfor dynamic content like slugs, dates, metadata
Related Documentation
- widget-loader Component - Async widget loading
- Widget System Overview - Widget architecture
- Unified Error Handling - Error handling patterns
- Live Preview Architecture - Preview system
- Validation Instant Feedback - Validation UX
- Multilingual Data Loading - Translation handling