frigate/.tasks/ui-config-rjsf/task.md
2026-02-27 09:37:56 -06:00

20 KiB

task slug created status
UI Configuration using react-jsonschema-form ui-config-rjsf 2026-01-21 planning

UI Configuration using react-jsonschema-form

Overview

Implement a comprehensive configuration UI for Frigate NVR using react-jsonschema-form (RJSF), driven by the existing Pydantic configuration schema. The UI should allow users to configure Frigate through a web interface instead of manually editing YAML files, with proper validation, reusable components, and clear visual indicators for global vs camera-level settings.

Research Findings

Key Components

Component Location Purpose
FrigateConfig frigate/config/config.py Root Pydantic model defining complete Frigate configuration
CameraConfig frigate/config/camera/camera.py Camera-level configuration model
FrigateBaseModel frigate/config/base.py Base model with extra="forbid" validation
Config API frigate/api/app.py PUT /config/set endpoint for saving config
Schema Endpoint frigate/api/app.py GET /config/schema.json - exposes Pydantic schema
Settings Page web/src/pages/Settings.tsx Main settings page with sidebar navigation
Form Components web/src/components/ui/form.tsx Existing react-hook-form based form components

Architecture

The Frigate configuration system is structured as follows:

  1. Pydantic Models: All configuration is defined using Pydantic v2 models in frigate/config/. The FrigateConfig class is the root model.

  2. Schema Generation: Pydantic automatically generates JSON Schema via model_json_schema(). The schema is already exposed at /api/config/schema.json and used by the ConfigEditor (monaco-yaml) for validation.

  3. Config API: The /config/set endpoint accepts configuration updates in two formats:

    • Query string parameters (e.g., ?cameras.front.detect.enabled=True)
    • JSON body via config_data field in AppConfigSetBody

    The JSON body is preferred.

  4. Frontend Stack:

    • React 18 with TypeScript
    • Radix UI primitives + Tailwind CSS (shadcn/ui patterns)
    • react-hook-form + zod for form validation
    • SWR for data fetching
    • react-i18next for translations
    • axios for API calls

Data Flow

  1. Config Loading: useSWR("config") fetches current config from /api/config
  2. Schema Loading: JSON Schema available at /api/config/schema.json
  3. Config Updates: PUT to /config/set with JSON body
  4. Real-time Updates: Some settings support requires_restart: 0 for live updates via WebSocket pub/sub

Reusable Config Sections (Global + Camera Level)

Based on schema analysis, these sections appear at BOTH global and camera levels using the same Pydantic model:

Section Model Notes
audio AudioConfig Full model reuse
detect DetectConfig Full model reuse, nested StationaryConfig
live CameraLiveConfig Full model reuse
motion MotionConfig Full model reuse
notifications NotificationConfig Full model reuse
objects ObjectConfig Full model reuse, nested FilterConfig, GenAIObjectConfig
record RecordConfig Full model reuse, nested EventsConfig, RetainConfig
review ReviewConfig Full model reuse, nested ReviewAlertsConfig, ReviewDetectionsConfig
snapshots SnapshotsConfig Full model reuse, nested SnapshotsRetainConfig
timestamp_style TimestampStyleConfig Full model reuse, nested ColorConfig

Different models at global vs camera level (require conditional handling):

Section Global Model Camera Model Notes
audio_transcription AudioTranscriptionConfig CameraAudioTranscriptionConfig Camera has subset
birdseye BirdseyeConfig BirdseyeCameraConfig Different fields
face_recognition FaceRecognitionConfig CameraFaceRecognitionConfig Camera has subset
ffmpeg FfmpegConfig CameraFfmpegConfig Camera adds inputs[]
lpr LicensePlateRecognitionConfig CameraLicensePlateRecognitionConfig Camera has subset
semantic_search SemanticSearchConfig CameraSemanticSearchConfig Completely different

Special Field Types Requiring Custom Widgets

Type Examples Widget Needed
Enums BirdseyeModeEnum, RetainModeEnum, RecordQualityEnum Select dropdown
list[str] objects.track, zones[], required_zones Tag input / Multi-select
Union[str, list[str]] mask fields Text or array input
dict[str, T] zones, objects.filters Key-value editor / Nested form
ColorConfig timestamp_style.color (RGB) Color picker
Coordinates/Masks zone.coordinates Polygon editor (existing)
Password fields mqtt.password Password input with show/hide

Current UI Patterns

  1. Settings Navigation: web/src/pages/Settings.tsx uses a sidebar with grouped sections
  2. Camera Selection: Many views accept selectedCamera prop for per-camera settings
  3. Form Pattern: Uses react-hook-form with zod schemas (see CameraReviewSettingsView.tsx)
  4. Save Pattern: Axios PUT to config/set?key=value with requires_restart flag
  5. Translations: All strings in web/public/locales/{lang}/views/ JSON files

Dependencies

Internal:

  • Existing form components: Form, FormField, FormItem, FormLabel, FormControl, FormMessage
  • UI primitives: Switch, Select, Input, Checkbox, Slider, Tabs
  • Existing hooks: useSWR, useTranslation, useOptimisticState

External (to add):

  • @rjsf/core (react-jsonschema-form core)
  • @rjsf/utils (utilities)
  • @rjsf/validator-ajv8 (JSON Schema validation)

Configuration

The /config/set API expects:

interface AppConfigSetBody {
  requires_restart: number; // 0 = live update, 1 = needs restart
  update_topic?: string; // For pub/sub notification
  config_data?: Record<string, any>; // Bulk config updates
}

Query string format: ?key.path.to.field=value (e.g., ?cameras.front.detect.enabled=True)

Tests

Test File Coverage
No existing React component tests found RJSF forms would benefit from testing
Pydantic validation tests implicit in config loading Schema validation ensures correctness

Implementation Plan

Goal

Users can configure all user-facing Frigate settings through a form-based UI with validation, clear global vs camera-level distinction, and proper handling of advanced settings.

Current State Analysis

  • Schema already exposed at /api/config/schema.json
  • Settings page structure exists with sidebar navigation
  • Form components exist (react-hook-form based)
  • No react-jsonschema-form currently installed
  • Config set API supports both query strings and JSON body

What We're NOT Doing

  • Replacing the raw YAML ConfigEditor (remains as advanced option)
  • Changing the Pydantic models structure
  • Modifying the config/set API endpoint
  • Auto-generating translations (manual translation required)

Prerequisites

  • Install @rjsf/core, @rjsf/utils, @rjsf/validator-ajv8
  • Create exclusion list and advanced settings list JSON files

Phases

# Phase Status Plan Notes
1 Schema Pipeline & RJSF Setup Done Install deps, create schema transformer, set up RJSF theme
2 Core Reusable Section Components Done Create shared components for detect, record, snapshots, etc.
3 Global Configuration View Done Build main config sections (mqtt, auth, database, etc.)
4 Camera Configuration with Tabs Done Multi-camera tabs, override indicators, section reuse
5 Advanced Settings & Exclusions Done Progressive disclosure, contributor-editable lists
6 Validation & Error Handling Done Inline errors, save blocking, API integration
7 Integration & Polish Done Settings page integration, translations, documentation

Status: Not Started → 📋 Planned → 🔄 In Progress → Done


Phase Details

Phase 1: Schema Pipeline & RJSF Setup

Overview: Install react-jsonschema-form dependencies, create a schema transformation layer to convert Pydantic JSON Schema to RJSF-compatible format with UI customizations.

Changes Required:

  1. Install Dependencies

    • Add to package.json: @rjsf/core, @rjsf/utils, @rjsf/validator-ajv8
  2. Create Schema Transformer

    • File: web/src/lib/config-schema/
    • Transform Pydantic schema to RJSF uiSchema
    • Handle nested $defs/references
    • Apply field ordering
  3. Create Custom RJSF Theme

    • File: web/src/components/config-form/theme/
    • Map RJSF templates to existing shadcn/ui components
    • Custom widgets for special types (color, coordinates, etc.)

Success Criteria:

  • RJSF renders basic form from schema
  • Existing UI component styling preserved
  • Schema fetching from /api/config/schema.json works

Phase 2: Core Reusable Section Components

Overview: Create composable section components for config areas that appear at both global and camera levels.

Changes Required:

  1. Section Component Architecture

    • File: web/src/components/config-form/sections/
    • Create: DetectSection, RecordSection, SnapshotsSection, MotionSection, ObjectsSection, ReviewSection, AudioSection, NotificationsSection, LiveSection, TimestampSection
  2. Each Section Component:

    • Accepts level: "global" | "camera" prop
    • Accepts cameraName?: string for camera context
    • Accepts showOverrideIndicator?: boolean
    • Uses shared RJSF form with section-specific uiSchema
  3. Override Detection Hook

    • File: web/src/hooks/use-config-override.ts
    • Compare camera value vs global default
    • Return override status for visual indicators
  4. Field Ordering and Layout Customization

Requirement: Field ordering and layout within each section must be easily customizable by contributors without requiring deep knowledge of RJSF internals.

Implementation Approach:

  • Each reusable section component (DetectSection, RecordSection, etc.) should define its own field ordering and layout configuration
  • This can be accomplished as a TypeScript constant within the section component file itself
  • The configuration should specify:
    • Field display order
    • Field grouping (which fields appear together)
    • Layout hints (e.g., multiple fields per row, nested groupings)
    • Any section-specific uiSchema customizations

Example Structure:

// In DetectSection.tsx or DetectSection.config.ts
export const detectSectionConfig = {
  fieldOrder: [
    "enabled",
    "fps",
    "width",
    "height",
    "max_disappeared",
    "stationary",
  ],
  fieldGroups: {
    resolution: ["width", "height"],
    performance: ["fps", "max_disappeared"],
  },
  // ... other layout hints
};

Success Criteria:

  • Contributors can reorder fields by editing a clear configuration structure
  • No need to modify RJSF internals or complex uiSchema objects directly
  • Layout changes are localized to single files per section

Success Criteria:

  • DetectSection renders identically at global and camera level
  • Override indicators show when camera differs from global
  • Adding new fields requires editing only section definition

Phase 3: Global Configuration View

Overview: Build the global configuration form for non-camera settings.

Changes Required:

  1. Global Config View

    • File: web/src/views/settings/GlobalConfigView.tsx
    • Sections: MQTT, Auth, Database, Telemetry, TLS, Proxy, Networking, UI, Detectors, Model, GenAI, Classification, Birdseye
  2. Per-Section Subforms

    • Each section as collapsible card
    • Progressive disclosure for advanced fields
    • Individual save buttons per section OR unified save
  3. Translations

    • File: web/public/locales/en/views/settings.json
    • Add keys for all config field labels/descriptions

Success Criteria:

  • All global-only settings configurable
  • Proper field grouping and labels
  • Validation errors inline

Phase 4: Camera Configuration with Tabs

Overview: Create per-camera configuration with tab navigation and override indicators.

Changes Required:

  1. Camera Config View

    • File: web/src/views/settings/CameraConfigView.tsx
    • Tab per camera
    • Uses reusable section components
  2. Override Visual Indicators

    • Badge/icon when field overrides global
    • "Reset to global default" action
    • Color coding (e.g., highlighted border)
  3. Camera-Specific Sections

    • FFmpeg inputs configuration
    • Masks and Zones (link to existing editor)
    • ONVIF settings

Success Criteria:

  • Switch between cameras via tabs
  • Clear visual distinction for overridden settings
  • Reset to global default works

Phase 5: Advanced Settings & Exclusions

Overview: Implement progressive disclosure and maintainable exclusion lists.

Changes Required:

  1. Exclusion System

    • Simple consts in the component for field names
    • Filter schema before rendering
    • Document exclusion format for contributors
  2. Advanced Fields Toggle

    • "Show Advanced Settings" switch per section
    • Simple consts in the component for advanced field names
    • Default collapsed state

Success Criteria:

  • Excluded fields never shown in UI
  • Advanced fields hidden by default

Phase 6: Validation & Error Handling

Overview: Ensure robust validation and user-friendly error messages.

Changes Required:

  1. Client-Side Validation

    • ajv8 validator with Pydantic schema
    • Custom error messages for common issues
    • Real-time validation on blur
  2. Server-Side Validation

    • Handle 400 responses from /config/set
    • Parse Pydantic validation errors
    • Map to form fields
  3. Save Blocking

    • Disable save button when invalid
    • Show error count badge
    • Scroll to first error on submit attempt

Success Criteria:

  • Invalid forms cannot be saved
  • Errors shown inline next to fields
  • Clear error messages (not technical schema errors)

Phase 7: Integration & Polish

Overview: Integrate into settings page, finalize translations, and document.

Changes Required:

  1. Settings Page Integration

    • Add new views to settingsGroups in Settings.tsx
    • Sidebar navigation updates
    • Route configuration
  2. Translations

    • All field labels and descriptions
    • Error messages
    • Section headers
  3. Documentation

    • User-facing docs for UI configuration
  4. Testing

    • Basic render tests for form components
    • Validation behavior tests
    • Save/cancel flow tests

Success Criteria:

  • Seamless navigation from settings page
  • All strings translated
  • Documentation complete

Testing Strategy

Project Maturity Level

Active Development - Frigate has extensive test infrastructure but limited frontend tests.

Unit Tests

  • Schema transformer functions
  • Override detection hook
  • Custom widgets
  • Coverage target: 70% for new components

Integration/Manual Tests

  • Full form render with live schema
  • Save/validation flow end-to-end
  • Camera tab switching
  • Override indicator accuracy
  • Mobile responsiveness

Rollback Plan

  1. All changes are additive - existing ConfigEditor remains functional
  2. New views can be feature-flagged if needed
  3. No database migrations required
  4. No backend changes required (uses existing API)

Component Hierarchy

web/src/
├── components/
│   └── config-form/
│       ├── theme/
│       │   ├── index.ts              # RJSF theme export
│       │   ├── templates/            # Base templates (ObjectFieldTemplate, etc.)
│       │   └── widgets/              # Custom widgets (ColorWidget, TagsWidget, etc.)
│       ├── sections/
│       │   ├── DetectSection.tsx     # Reusable for global + camera
│       │   ├── RecordSection.tsx
│       │   ├── SnapshotsSection.tsx
│       │   ├── MotionSection.tsx
│       │   ├── ObjectsSection.tsx
│       │   ├── ReviewSection.tsx
│       │   ├── AudioSection.tsx
│       │   ├── NotificationsSection.tsx
│       │   ├── LiveSection.tsx
│       │   └── TimestampSection.tsx
│       └── ConfigForm.tsx            # Main form wrapper
├── lib/
│   └── config-schema/
│       ├── index.ts                  # Schema utilities
│       ├── transformer.ts            # Pydantic -> RJSF schema
├── hooks/
│   └── use-config-override.ts        # Override detection
└── views/
    └── settings/
        ├── GlobalConfigView.tsx      # Global settings form
        └── CameraConfigView.tsx      # Per-camera tabs form

Key Design Decisions

  1. RJSF over custom forms: Leverage schema-driven forms for maintainability and automatic updates when Pydantic models change.

  2. Reusable sections via composition: Same component renders at global and camera level, with props controlling context and override indicators.

  3. Existing UI primitives: Custom RJSF theme wraps existing shadcn/ui components for visual consistency.

  4. Incremental adoption: Existing settings views remain, new RJSF views added alongside.