CMS content authoring
RichTextEditor
Lightweight, dependency-free WYSIWYG (contentEditable) producing an HTML string: bold/italic/underline/strike, H2/H3, lists, links. For long-form editorial work consider wrapping a full editor engine — this covers typical CMS content fields.
⚠️ Always sanitize the HTML server-side before rendering it to other users.
Select some text and use the toolbar — bold, lists, links…
import { RichTextEditor } from '@gg-software/cms';
<RichTextEditor value={html} onChange={setHtml} placeholder="Write something…" />;| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | controlled HTML value |
defaultValue | string | — | initial HTML for uncontrolled use |
onChange | ((html: string) => void) | — | called with the editor's HTML after every edit |
placeholder | string | Write something… | |
disabled | boolean | false | |
minHeight | string | number | 160 | editing area min-height (default 160) |
className | string | — |
* required · generated from packages/cms/src/content/RichTextEditor.tsx
MarkdownEditor
Markdown with a live preview — side-by-side panes (default), write/preview tabs, or none.
The built-in renderer (exported as renderMarkdown) covers headings, bold, italic, code,
fenced blocks, links and lists, and HTML-escapes the input.
Hello
Write markdown on the left, see the preview on the right.
- lists
code
import { MarkdownEditor, renderMarkdown } from '@gg-software/cms';
<MarkdownEditor value={markdown} onChange={setMarkdown} preview="side" />;
renderMarkdown('# Hi'); // "<h1>Hi</h1>"| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | controlled markdown value |
defaultValue | string | — | initial markdown for uncontrolled use |
onChange | ((markdown: string) => void) | — | |
preview | "side" | "tabs" | "none" | side | side-by-side panes or write/preview tabs (default "side") |
placeholder | string | Write markdown… | |
rows | number | 10 | |
disabled | boolean | false | |
className | string | — |
* required · generated from packages/cms/src/content/MarkdownEditor.tsx
MediaLibrary
Thumbnail grid of uploaded assets with search, selection and an optional upload action.
import { MediaLibrary } from '@gg-software/cms';
<MediaLibrary
assets={assets}
multiple
onSelectionChange={(ids) => setPicked(ids)}
onUpload={(files) => uploadAll(files)}
/>;| Prop | Type | Default | Description |
|---|---|---|---|
assets* | MediaAsset[] | — | |
onSelect | ((asset: MediaAsset) => void) | — | single select: called with the clicked asset |
multiple | boolean | false | allow selecting several (controlled by selectedIds + onSelectionChange) |
selectedIds | (string | number)[] | — | selected asset ids (single select highlights too) |
onSelectionChange | ((ids: (string | number)[]) => void) | — | |
searchable | boolean | true | show the search box (default true) |
onUpload | ((files: File[]) => void) | — | shows an Upload button wired to a hidden file input |
accept | string | image/* | accept attribute for uploads (default "image/*") |
emptyText | ReactNode | No assets yet | |
className | string | — |
* required · generated from packages/cms/src/content/MediaLibrary.tsx
ImageUploader
Image upload with instant preview: drag-drop/click to pick, preview, remove/replace.
Reports the raw File — the actual upload is yours.
import { ImageUploader } from '@gg-software/cms';
<ImageUploader
value={product.imageUrl} // existing image
onChange={(file) => setPendingUpload(file)} // null = removed
maxSize={2 * 1024 * 1024}
hint="PNG or JPG, up to 2 MB"
/>;| Prop | Type | Default | Description |
|---|---|---|---|
value | string | null | — | existing image URL to show before any upload (e.g. the saved value) |
onChange | ((file: File | null) => void) | — | called with the picked file, or null when removed |
accept | string | image/* | accept attribute (default "image/*") |
maxSize | number | — | reject files larger than this many bytes |
hint | ReactNode | — | |
disabled | boolean | false | |
className | string | — |
* required · generated from packages/cms/src/content/ImageUploader.tsx
SEOFields
Meta title / description / slug with recommended-length counters, auto-slug from the title
(utils slugify, stops once the slug is edited manually) and optional Open Graph
overrides.
import { SEOFields } from '@gg-software/cms';
const [seo, setSeo] = useState<SEOValue>({ title: '', description: '', slug: '' });
<SEOFields value={seo} onChange={setSeo} titleMax={60} descriptionMax={160} />;| Prop | Type | Default | Description |
|---|---|---|---|
value* | SEOValue | — | |
onChange* | (value: SEOValue) => void | — | |
titleMax | number | 60 | recommended title length, drives the counter (default 60) |
descriptionMax | number | 160 | recommended description length (default 160) |
autoSlug | boolean | true | keep the slug in sync with the title until the slug is edited manually (default true) |
showOpenGraph | boolean | true | show the Open Graph override fields (default true) |
disabled | boolean | false | |
className | string | — |
* required · generated from packages/cms/src/content/SEOFields.tsx
ContentScheduler
Draft / published / scheduled status with publish and optional unpublish date+time.
import { ContentScheduler } from '@gg-software/cms';
const [schedule, setSchedule] = useState<ScheduleValue>({ status: 'draft' });
<ContentScheduler value={schedule} onChange={setSchedule} />;| Prop | Type | Default | Description |
|---|---|---|---|
value* | ScheduleValue | — | |
onChange* | (value: ScheduleValue) => void | — | |
showUnpublish | boolean | true | offer the unpublish date (default true) |
disabled | boolean | false | |
className | string | — |
* required · generated from packages/cms/src/content/ContentScheduler.tsx