A React component library for the Sunbird Collection Editor — build, organise and manage hierarchical content collections (courses, textbooks, playlists) inside any React 18 application or as a framework-agnostic web component.
npm install @project-sunbird/collection-editor-reactPeer dependencies (install separately if not already present):
npm install react@^18 react-dom@^18import { CollectionEditor } from '@project-sunbird/collection-editor-react';
import '@project-sunbird/collection-editor-react/dist/style.css';
const config = {
context: {
authToken: 'your-bearer-token', // omit when using a server-side proxy
userId: 'user-id',
sid: 'session-id',
did: 'device-id',
channel: 'channel-id',
pdata: { id: 'your.app.id', ver: '1.0' },
env: 'collection_editor',
identifier: 'do_123456789', // content identifier to load
contentId: 'do_123456789',
},
config: {
mode: 'edit', // 'edit' | 'review' | 'read' | 'sourcingreview'
objectType: 'Collection',
primaryCategory: 'Content Playlist',
maxDepth: 4,
},
};
export default function App() {
return (
<CollectionEditor
{...config}
onToolbarEvent={({ action, data }) => {
if (action === 'back') window.history.back();
}}
/>
);
}Register once (e.g. in your app entry point), then use the custom element anywhere:
import { registerCollectionEditor } from '@project-sunbird/collection-editor-react';
import '@project-sunbird/collection-editor-react/dist/style.css';
registerCollectionEditor(); // registers <sb-collection-editor><sb-collection-editor id="editor"></sb-collection-editor>
<script>
const el = document.getElementById('editor');
el.config = JSON.stringify({
context: { /* ... */ },
config: { mode: 'edit', objectType: 'Collection' },
});
el.onToolbarEvent = (e) => console.log('toolbar:', e.detail);
</script>You can pass a custom tag name to avoid collisions:
registerCollectionEditor('my-collection-editor');| Prop | Type | Required | Description |
|---|---|---|---|
context |
IContext |
✅ | Runtime context — user, session, channel, framework |
config |
IConfig |
✅ | Editor behaviour — mode, objectType, depth, category |
metadata |
Record<string, unknown> |
— | Pre-loaded content metadata |
apiBaseUrl |
string |
— | Base URL for all API calls. Omit when using a server-side proxy |
onToolbarEvent |
(e: { action: ToolbarAction; data?: unknown }) => void |
— | Fired on every toolbar button click |
onContentAdded |
(item: unknown, targetNodeId: string) => void |
— | Fired when content is added from library |
onHierarchySaved |
(hierarchy: unknown) => void |
— | Fired after a successful hierarchy save |
onError |
(error: Error) => void |
— | Fired on unrecoverable editor errors |
interface IContext {
authToken: string; // Bearer token; use '' when auth is cookie-based
userId: string;
sid: string; // Session ID
did: string; // Device ID
channel: string; // Org channel / hashTagId
pdata: { id: string; ver: string; pid?: string }; // Producer data (telemetry)
env: string; // e.g. 'collection_editor'
contentId?: string; // Identifier of the collection to load
identifier?: string; // Alias for contentId
framework?: string; // Org framework ID
targetFWIds?: string[]; // Target framework IDs
uid?: string;
rollup?: Record<string, string>;
tags?: string[];
}interface IConfig {
mode: 'edit' | 'review' | 'read' | 'sourcingreview';
objectType: string; // e.g. 'Collection'
primaryCategory?: string; // e.g. 'Content Playlist', 'Course'
framework?: string[];
targetFWIds?: string[];
maxDepth?: number; // Max folder nesting depth (default: 4)
allowContentUnderRoot?: boolean;
hierarchy?: Record<string, unknown>;
defaultFields?: Record<string, unknown>;
}type ToolbarAction =
| 'back' | 'preview' | 'saveCollection' | 'publish'
| 'sendForReview' | 'reject' | 'sendBackForCorrections'
| 'sourcingApprove' | 'sourcingReject'
| 'addUnit' | 'addSubUnit'
| 'manageCollaborators' | 'csvUpload'
| 'onFormValueChange' | 'onFormStatusChange';By default all API calls are made relative to the page origin (works with a server-side proxy). To point directly at a backend:
import { setApiBaseUrl } from '@project-sunbird/collection-editor-react';
setApiBaseUrl('https://api.your-sunbird-instance.com');Or pass it inline — the editor calls setApiBaseUrl automatically on mount:
<CollectionEditor
{...config}
apiBaseUrl="https://api.your-sunbird-instance.com"
/>The editor exposes its internal Zustand stores for advanced integration scenarios — for example, reading the current tree selection from outside the component:
import { useEditorStore, useTreeStore, useLibraryStore } from '@project-sunbird/collection-editor-react';
// Inside a React component
const selectedNodeId = useTreeStore((s) => s.selectedNodeId);
const editorMode = useEditorStore((s) => s.editorMode);The stylesheet must be imported once in your app — it is not auto-injected:
import '@project-sunbird/collection-editor-react/dist/style.css';# Clone the monorepo
git clone https://github.com/Sunbird-Ed/sunbird-collection-editor.git
cd sunbird-collection-editor/projects/collection-editor-react
# Install
npm install
# Dev server (proxies /action and /api to localhost:3000)
npm run dev
# Build library
npm run build
# Run tests
npm test| Dependency | Version |
|---|---|
| React | 18.x |
| React DOM | 18.x |
| Node | 18+ |
MIT — see the repository root for the full licence text.