Special Features
Dynamic Form State Management with ID Assignment
Overview
Sitepins implements a sophisticated form state management system that uses unique ID assignment to handle complex, dynamic forms with nested objects, arrays, and real-time updates. This system ensures Reactβs reconciliation works correctly while maintaining clean data for persistence.
The ID Assignment System
Core Functions:
assignUniqueId(): Adds unique IDs to form data for UI operationsrevertToOriginal(): Removes IDs before saving to backend/Gituuid(): Generates cryptographically secure unique identifiers
Why We Add IDs
1. React Key Management
// Each dynamic form field needs a stable key for React's reconciliation
{
formFields.map((field) => <FormField key={field.id} {...field} />);
}2. Dynamic Array Operations
- Users can add/remove/reorder array items in real-time
- Each array item needs a unique identifier for tracking changes
- Enables smooth animations and state preservation during operations
3. Complex Nested State
// Example: Blog post with dynamic tags array
const blogPost = {
title: { id: "uuid-1", value: "My Blog Post" },
tags: [
{ id: "uuid-2", value: "react" },
{ id: "uuid-3", value: "typescript" },
],
author: {
id: "uuid-4",
value: {
name: { id: "uuid-5", value: "John Doe" },
email: { id: "uuid-6", value: "john@example.com" },
},
},
};4. Optimistic Updates
- IDs allow tracking which specific fields are being updated
- Enables optimistic UI updates before server confirmation
- Maintains form state during async operations
Why We Remove IDs
1. Clean Data Persistence
// Before saving to Git/backend, remove UI-specific IDs
const cleanData = revertToOriginal(formData);
// Result: { title: "My Blog Post", tags: ["react", "typescript"] }2. Data Integrity
- Ensures consistent data structure across environments
- Prevents conflicts when multiple users edit same content
- Maintains backward compatibility with existing content
Complete Workflow
Step-by-Step Process:
- Load Phase
// Load content from Git/API
const rawContent = await fetchContent();
// Add IDs for UI operations
const formData = assignUniqueId(rawContent);- Edit Phase
// User interacts with form fields
// React uses IDs for efficient reconciliation
// State updates maintain component focus and animations- Save Phase
// Remove IDs before persistence
const cleanData = revertToOriginal(formData);
// Save to backend/Git
await saveContent(cleanData);Implementation Details
ID Assignment Logic:
export function assignUniqueId(input: any): any {
if (Array.isArray(input)) {
return input.map((item) => {
if (typeof item === "object") {
return {
id: uuid(),
value: assignUniqueId(item),
};
}
return assignUniqueId(item);
});
}
if (typeof input === "object" && input !== null) {
return Object.keys(input).reduce((acc, key) => {
const value = input[key];
if (value instanceof Date) {
return {
...acc,
[key]: {
id: uuid(),
value: new ISODate(value.toISOString()),
},
};
}
return {
...acc,
[key]: {
value: typeof value === "object" ? assignUniqueId(value) : value,
id: uuid(),
},
};
}, {});
}
return {
value: input,
id: uuid(),
};
}ID Removal Logic:
export function revertToOriginal(input: any): any {
if (Array.isArray(input)) {
return input.map((item) => revertToOriginal(item));
}
if (typeof input === "object" && input !== null) {
// Check if object has ID wrapper structure
if ("id" in input && "value" in input && Object.keys(input).length === 2) {
return revertToOriginal(input.value);
}
// Process regular object
return Object.entries(input).reduce((acc, [key, value]) => {
acc[key] = revertToOriginal(value);
return acc;
}, {} as Record<string, any>);
}
return input;
}Performance Considerations
Memory Efficiency:
- IDs are temporary and cleaned up during save operations
- Minimal memory overhead for enhanced UX
- Garbage collection handles cleanup automatically
React Performance:
- Stable keys prevent unnecessary re-renders
- Smooth animations during array operations
- Preserved component state during updates
Bundle Size:
- UUID generation uses crypto API (minimal size impact)
- Helper functions are tree-shakeable
- No external dependencies required
Usage in Sitepins Components
Editor Wrapper:
// src/app/(protected)/(site)/[orgId]/[projectId]/files/[...file]/_components/editor-wrapper.tsx
const [state, setState] = useState<State | undefined>({
data: assignUniqueId(data), // Add IDs for form operations
page_content: content ?? "",
});
// On save, IDs are removed in commit logic
const cleanData = revertToOriginal(state.data);Preview Data Component:
// src/app/(protected)/(site)/[orgId]/[projectId]/files/[...file]/_components/preview-data.tsx
const createNewItem = (fields: Field[]) => {
return fields.reduce((acc, field) => {
acc[field.name] = { value: "", id: uuid() }; // New items get IDs
return acc;
}, {} as any);
};This system enables Sitepins to provide a smooth, responsive content editing experience while maintaining clean, portable content that works across different environments and platforms.
AI-Powered Content Generation
// AI content generation endpoint
export async function POST(request: Request) {
const { prompt, type, context } = await request.json();
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [
{ role: "system", content: getSystemPrompt(type) },
{ role: "user", content: prompt },
],
max_tokens: 2000,
temperature: 0.7,
});
return NextResponse.json({
content: response.choices[0]?.message?.content,
usage: response.usage,
});
}Advanced Editor Features
- Monaco Editor: Code editing with syntax highlighting
- Plate Editor: Rich text editing with markdown support
- Live Preview: Real-time content preview
- Collaboration: Multi-user editing capabilities
- Object Visual Editor (
preview-data.tsx):
- This is the main component responsible for rendering the entire object visual editor in Sitepins.
- Dynamically renders all fields, nested objects, and arrays based on a provided schema.
- Manages breadcrumb navigation for deep/nested editing and user-friendly navigation.
- Handles updates, additions, and deletions of data visually, supporting all field types (string, number, date, media, gallery, array, object, boolean).
- Ensures each field/item has a unique ID for React reconciliation and state management.
- Provides UI controls for editing, adding, and removing items, enabling users to visually edit complex, nested data structures.
- Core logic for dynamic forms and content editing is implemented here; extend or customize this file to change editor behavior.