Skip to Content
CMSContent

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…" />;
PropTypeDefaultDescription
valuestringcontrolled HTML value
defaultValuestringinitial HTML for uncontrolled use
onChange((html: string) => void)called with the editor's HTML after every edit
placeholderstringWrite something…
disabledbooleanfalse
minHeightstring | number160editing area min-height (default 160)
classNamestring

* 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>"
PropTypeDefaultDescription
valuestringcontrolled markdown value
defaultValuestringinitial markdown for uncontrolled use
onChange((markdown: string) => void)
preview"side" | "tabs" | "none"sideside-by-side panes or write/preview tabs (default "side")
placeholderstringWrite markdown…
rowsnumber10
disabledbooleanfalse
classNamestring

* 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)} />;
PropTypeDefaultDescription
assets*MediaAsset[]
onSelect((asset: MediaAsset) => void)single select: called with the clicked asset
multiplebooleanfalseallow selecting several (controlled by selectedIds + onSelectionChange)
selectedIds(string | number)[]selected asset ids (single select highlights too)
onSelectionChange((ids: (string | number)[]) => void)
searchablebooleantrueshow the search box (default true)
onUpload((files: File[]) => void)shows an Upload button wired to a hidden file input
acceptstringimage/*accept attribute for uploads (default "image/*")
emptyTextReactNodeNo assets yet
classNamestring

* 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" />;
PropTypeDefaultDescription
valuestring | nullexisting 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
acceptstringimage/*accept attribute (default "image/*")
maxSizenumberreject files larger than this many bytes
hintReactNode
disabledbooleanfalse
classNamestring

* 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.

22/60
37/160
/Lowercase letters, numbers and dashes.
Falls back to the meta title.
Falls back to the meta description.
import { SEOFields } from '@gg-software/cms'; const [seo, setSeo] = useState<SEOValue>({ title: '', description: '', slug: '' }); <SEOFields value={seo} onChange={setSeo} titleMax={60} descriptionMax={160} />;
PropTypeDefaultDescription
value*SEOValue
onChange*(value: SEOValue) => void
titleMaxnumber60recommended title length, drives the counter (default 60)
descriptionMaxnumber160recommended description length (default 160)
autoSlugbooleantruekeep the slug in sync with the title until the slug is edited manually (default true)
showOpenGraphbooleantrueshow the Open Graph override fields (default true)
disabledbooleanfalse
classNamestring

* required · generated from packages/cms/src/content/SEOFields.tsx

ContentScheduler

Draft / published / scheduled status with publish and optional unpublish date+time.

Status
Publish on
At
Unpublish on
Optional — leave empty to keep it live.
At
import { ContentScheduler } from '@gg-software/cms'; const [schedule, setSchedule] = useState<ScheduleValue>({ status: 'draft' }); <ContentScheduler value={schedule} onChange={setSchedule} />;
PropTypeDefaultDescription
value*ScheduleValue
onChange*(value: ScheduleValue) => void
showUnpublishbooleantrueoffer the unpublish date (default true)
disabledbooleanfalse
classNamestring

* required · generated from packages/cms/src/content/ContentScheduler.tsx