Plugins
Object UI supports a powerful plugin system that allows you to extend the framework with additional components. Plugins are separate packages that load on-demand, keeping your main application bundle small while providing rich functionality.
Overview
Plugins are lazy-loaded component packages that:
- Auto-register components when imported
- Lazy-load heavy dependencies on-demand
- Keep bundles small - only load when needed
- Are type-safe with full TypeScript support
- Follow best practices with built-in loading states
Official Plugins
Object UI provides 14+ official plugins for common use cases:
Data Visualization & Dashboards
@object-ui/plugin-charts
Data visualization components powered by Recharts.
- Bar, line, area, and pie charts
- Responsive design
- Customizable colors
- Lazy-loaded (~80 KB)
@object-ui/plugin-dashboard
Dashboard layouts with metric cards and widgets.
- Dashboard grid layouts
- Metric/KPI cards with trends
- Widget system
- Lazy-loaded (~22 KB)
@object-ui/plugin-timeline
Timeline component with multiple layout variants.
- Vertical, horizontal layouts
- Customizable markers
- Date formatting
- Lazy-loaded (~20 KB)
@object-ui/plugin-gantt
Gantt chart for project visualization.
- Task dependencies
- Progress tracking
- ObjectQL integration
- Lazy-loaded (~40 KB)
@object-ui/plugin-calendar
Calendar visualization for events.
- Month/week/day views
- Event management
- ObjectQL integration
- Lazy-loaded (~25 KB)
@object-ui/plugin-map
Map visualization with markers.
- Interactive maps
- Location markers
- ObjectQL integration
- Lazy-loaded (~60 KB)
Data Management
@object-ui/plugin-grid
Advanced data grid with sorting, filtering, and pagination.
- Column sorting and filtering
- Pagination controls
- Row selection
- Lazy-loaded (~45 KB)
@object-ui/plugin-aggrid
Enterprise data grid powered by AG Grid.
- Advanced filtering
- Row editing
- Multiple themes
- Lazy-loaded (~150 KB)
@object-ui/plugin-form
Advanced form builder with validation.
- Multi-step forms
- Field validation
- Custom field types
- Lazy-loaded (~28 KB)
@object-ui/plugin-view
ObjectQL-integrated views for automatic CRUD.
- Auto-generated forms and grids
- CRUD operations
- Field mapping
- Lazy-loaded (~35 KB)
Content & Editing
@object-ui/plugin-editor
Code editor component powered by Monaco Editor.
- Syntax highlighting for 100+ languages
- IntelliSense and code completion
- Multiple themes
- Lazy-loaded (~120 KB)
@object-ui/plugin-markdown
Markdown renderer with GitHub Flavored Markdown support.
- GitHub Flavored Markdown
- XSS protection
- Code syntax highlighting
- Lazy-loaded (~30 KB)
@object-ui/plugin-chatbot
Chat interface component.
- Message history
- User and assistant roles
- Timestamps and avatars
- Lazy-loaded (~35 KB)
Workflows & Tasks
@object-ui/plugin-kanban
Kanban board component with drag-and-drop powered by @dnd-kit.
- Drag and drop cards between columns
- Column limits (WIP limits)
- Card badges for status/priority
- Lazy-loaded (~100 KB)
How Plugins Work
Lazy Loading Architecture
Plugins use React's lazy() and Suspense to load heavy dependencies on-demand:
// The plugin structure
import React, { Suspense } from 'react'
import { Skeleton } from '@object-ui/components'
// Lazy load the heavy implementation
const LazyEditor = React.lazy(() => import('./MonacoImpl'))
export const CodeEditorRenderer = (props) => (
<Suspense fallback={<Skeleton className="w-full h-[400px]" />}>
<LazyEditor {...props} />
</Suspense>
)Benefits:
- Smaller initial bundle: Main app loads faster
- Progressive loading: Components load when needed
- Better UX: Loading skeletons while chunks download
- Automatic code splitting: Vite handles chunking
Bundle Impact
| Plugin | Initial Load | Lazy Load | Description |
|---|---|---|---|
| plugin-editor | ~0.2 KB | ~120 KB | Monaco editor |
| plugin-charts | ~0.2 KB | ~80 KB | Recharts visualization |
| plugin-kanban | ~0.2 KB | ~100 KB | Drag-and-drop board |
| plugin-markdown | ~0.2 KB | ~30 KB | Markdown rendering |
| plugin-aggrid | ~0.2 KB | ~150 KB | AG Grid data grid |
| plugin-dashboard | ~0.2 KB | ~22 KB | Dashboard layouts |
| plugin-form | ~0.2 KB | ~28 KB | Form builder |
| plugin-grid | ~0.2 KB | ~45 KB | Data grid |
| plugin-view | ~0.2 KB | ~35 KB | ObjectQL views |
| plugin-timeline | ~0.2 KB | ~20 KB | Timeline layouts |
| plugin-chatbot | ~0.2 KB | ~35 KB | Chat interface |
| plugin-calendar | ~0.2 KB | ~25 KB | Calendar views |
| plugin-gantt | ~0.2 KB | ~40 KB | Gantt charts |
| plugin-map | ~0.2 KB | ~60 KB | Map visualization |
Without lazy loading, all this code would be in your main bundle!
Auto-Registration
Plugins automatically register their components when imported:
// In the plugin's index.tsx
import { ComponentRegistry } from '@object-ui/core'
ComponentRegistry.register('code-editor', CodeEditorRenderer)You just need to import the plugin once:
// In your App.tsx or main.tsx
import '@object-ui/plugin-editor'
import '@object-ui/plugin-charts'
import '@object-ui/plugin-kanban'
import '@object-ui/plugin-markdown'
import '@object-ui/plugin-dashboard'
import '@object-ui/plugin-form'
import '@object-ui/plugin-grid'
// ... import other plugins as neededNow all plugin components are available in your schemas!
Creating Custom Plugins
You can create your own plugins following the same pattern:
1. Create Package Structure
mkdir -p packages/plugin-myfeature/src
cd packages/plugin-myfeature2. Create Heavy Implementation
// src/MyFeatureImpl.tsx
import HeavyLibrary from 'heavy-library'
export default function MyFeatureImpl(props) {
return <HeavyLibrary {...props} />
}3. Create Lazy Wrapper
// src/index.tsx
import React, { Suspense } from 'react'
import { ComponentRegistry } from '@object-ui/core'
import { Skeleton } from '@object-ui/components'
// Lazy load implementation
const LazyFeature = React.lazy(() => import('./MyFeatureImpl'))
// Create renderer with Suspense
export const MyFeatureRenderer = (props) => (
<Suspense fallback={<Skeleton className="w-full h-[300px]" />}>
<LazyFeature {...props} />
</Suspense>
)
// Auto-register
ComponentRegistry.register('my-feature', MyFeatureRenderer)
// Export for manual use
export const myFeatureComponents = {
'my-feature': MyFeatureRenderer
}4. Add TypeScript Types
// src/types.ts
import type { BaseSchema } from '@object-ui/types'
export interface MyFeatureSchema extends BaseSchema {
type: 'my-feature'
customProp?: string
}5. Configure Build
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
export default defineConfig({
plugins: [react()],
build: {
lib: {
entry: resolve(__dirname, 'src/index.tsx'),
name: 'ObjectUIPluginMyFeature',
fileName: (format) => `index.${format}.js`
},
rollupOptions: {
external: [
'react',
'react-dom',
'@object-ui/components',
'@object-ui/core'
],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
}
}
})6. Add Package.json
{
"name": "@object-ui/plugin-myfeature",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.umd.js",
"module": "./dist/index.es.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.es.js",
"require": "./dist/index.umd.js",
"types": "./dist/index.d.ts"
}
},
"files": ["dist"],
"scripts": {
"build": "vite build && tsc --emitDeclarationOnly"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"dependencies": {
"heavy-library": "^1.0.0"
},
"devDependencies": {
"@object-ui/components": "workspace:*",
"@object-ui/core": "workspace:*",
"@object-ui/types": "workspace:*",
"typescript": "^5.0.0",
"vite": "^5.0.0"
}
}Best Practices
1. Keep Entry Point Light
The main index file should only contain:
- Lazy loading wrapper
- Component registration
- Type exports
Heavy imports go in the *Impl.tsx file.
2. Provide Good Loading States
Always show a meaningful skeleton while loading:
<Suspense fallback={
<Skeleton className="w-full h-[400px]" />
}>
<LazyComponent {...props} />
</Suspense>3. Export Types
Make your plugin type-safe:
export type { MyFeatureSchema } from './types'4. Document Your Plugin
Include a README with:
- Installation instructions
- Usage examples
- Schema API reference
- Bundle size information
5. Test Lazy Loading
Verify that:
- The main bundle is small (~200 bytes)
- The lazy chunk is separate
- Components load correctly when rendered
pnpm build
ls -lh dist/Plugin vs Component Package
Use a Plugin when:
- The component depends on large libraries (>50 KB)
- Not all apps will use this component
- You want on-demand loading
Use regular Components when:
- The component is lightweight
- Most apps will use it
- It's part of core functionality
Troubleshooting
Plugin not loading
Check that you imported it in your app:
import '@object-ui/plugin-myfeature'TypeScript errors
Make sure types are exported:
export type { MyFeatureSchema } from '@object-ui/plugin-myfeature'Bundle size too large
Check that the implementation is in a separate file:
✅ src/index.tsx (light, uses React.lazy)
✅ src/MyFeatureImpl.tsx (heavy, imported lazily)Component not registering
Check that ComponentRegistry.register() is called at the module level:
// ✅ Good - runs on import
ComponentRegistry.register('my-feature', MyFeatureRenderer)
// ❌ Bad - never runs
export function registerComponents() {
ComponentRegistry.register('my-feature', MyFeatureRenderer)
}Related Documentation
- Component Registry - Understanding the registry
- Schema Rendering - How schemas become UI
- Lazy-Loaded Plugins Architecture - Deep dive
- Creating Components - Component development
- Create Plugin Utility - Scaffold new plugins quickly
- CLI Tool - Test plugins with the CLI
- All Utilities - Complete toolkit for development
Next Steps
- Install official plugins you need
- Try creating a custom plugin
- Share your plugins with the community
- Contribute new plugins to Object UI