Types reference

The TypeScript types theme + plugin authors most often use, with the canonical definitions and what's actually in each field.

The TypeScript types theme + plugin authors most often use, with the canonical definitions and what's actually in each field.

For the exhaustive list see the source: src/core/types.ts (admin-side types) and src/themes/types.ts (theme-side types).

Content types

Post

The same shape covers both posts (type: "post") and pages (type: "page").

TS
interface Post {
  id: string;
  type: "post" | "page";
  title: string;
  slug: string;
  contentMarkdown: string;
  excerpt?: string;
  heroMediaId?: string;            // Media doc id; resolve via `media.get(heroMediaId)`
  authorId: string;                // User uid
  termIds: string[];               // Category + tag ids — at most ONE category
  primaryTermId?: string;          // The category id (denormalised — affects URL)
  status: "draft" | "online";
  seo?: SeoMeta;
  createdAt?: Timestamp;
  updatedAt?: Timestamp;
  publishedAt?: Timestamp;
  lastPublishedPath?: string;      // current live path (e.g. "news/launch.html")
  previousPublishedPaths?: string[]; // paths whose deletion failed — retried on next publish
  lastPublishedHash?: string;      // sha256 of rendered HTML — skip-upload optimisation
  legacyUrl?: string;              // imported from external CMS (WP, etc.)
}

interface SeoMeta {
  title?: string;
  description?: string;
  ogImage?: string;
}

Term

Categories and tags share one schema, distinguished by type.

TS
interface Term {
  id: string;
  type: "category" | "tag";
  name: string;
  slug: string;
  description?: string;
  parentId?: string;               // for hierarchical categories
  createdAt?: Timestamp;
  updatedAt?: Timestamp;
  lastPublishedPath?: string;      // for category archives ("news/index.html")
}

Media

TS
interface Media {
  id: string;
  filename: string;                // user-uploaded filename
  alt?: string;
  caption?: string;
  uploadedAt: Timestamp;
  uploadedBy: string;              // user uid
  // Modern format (every upload after the variant pipeline):
  formats?: Record<string, MediaVariant>;
  defaultFormat?: string;
  originalSlug?: string;           // slug + 6-char hex suffix
  // Legacy single-URL format (uploaded before variant pipeline):
  url?: string;
  storagePath?: string;
}

interface MediaVariant {
  url: string;
  width: number;
  height: number;
  bytes: number;
}

For consumption in templates, use mediaToView(media) from @flexweg/cms-runtime to convert to MediaView — handles the legacy / modern divergence transparently.

MediaView

TS
interface MediaView {
  alt?: string;
  caption?: string;
  default: string;                 // name of the default variant
  formats: Record<string, { url: string; width: number; height: number }>;
}

Pick a specific URL via pickFormat(view, name) or pickMediaUrl(view).

SiteSettings

TS
interface SiteSettings {
  title: string;
  description?: string;
  language?: string;               // BCP-47, e.g. "en", "fr-FR"
  baseUrl?: string;                // public URL — required for sitemaps, RSS, search
  activeThemeId: string;
  themeConfigs?: Record<string, unknown>;
  enabledPlugins?: Record<string, boolean>;
  pluginConfigs?: Record<string, unknown>;
  homeMode?: "latest-posts" | "static-page";
  homePageId?: string;             // required when homeMode === "static-page"
  postsPerPage?: number;
  paginationMode?: "global" | "paginated";
  menus?: { header: MenuItem[]; footer: MenuItem[] };
  socials?: SocialEntry[];
  // …
}

The pluginConfigs.<id> map carries each plugin's config. The themeConfigs.<id> map carries each theme's config.

MenuItem

TS
interface MenuItem {
  id: string;                      // local-only stable id
  kind: "post" | "page" | "category" | "tag" | "url";
  ref?: string;                    // post/page/term id — when kind isn't "url"
  label?: string;                  // override; defaults to entity name
  href?: string;                   // direct URL — when kind === "url"
  target?: "_blank" | "_self";
  children?: MenuItem[];           // nested submenus
}

User-related

TS
type UserRole = "admin" | "editor";
type AdminLocale = "en" | "fr" | "de" | "es" | "nl" | "pt" | "ko";

interface UserRecord {
  uid: string;
  email: string;
  displayName?: string;
  firstName?: string;
  lastName?: string;
  role: UserRole;
  bio?: string;
  title?: string;                  // job title shown publicly
  avatarMediaId?: string;
  socials?: SocialEntry[];
  preferences?: UserPreferences;
}

interface UserPreferences {
  adminLocale?: AdminLocale;
}

Theme-side types

SiteContext

Every template + BaseLayout receives this:

TS
interface SiteContext {
  settings: SiteSettings;
  resolvedMenus: {
    header: ResolvedMenuItem[];
    footer: ResolvedMenuItem[];
  };
  themeCssPath: string;            // "theme-assets/<active-id>.css"
  themeConfig?: unknown;           // active theme's resolved config
}

interface ResolvedMenuItem {
  label: string;
  href: string;                    // absolute path on Flexweg
  target?: "_blank" | "_self";
  children?: ResolvedMenuItem[];
}

BaseLayoutProps

TS
interface BaseLayoutProps {
  site: SiteContext;
  pageTitle: string;
  pageDescription?: string;
  ogImage?: string;
  currentPath: string;             // e.g. "news/launch.html"
  extraHead?: string;              // unused (sentinel-replaced post-render)
  children: ReactNode;
}

Template-specific props

See Templates and props for HomeTemplateProps, SingleTemplateProps, CategoryTemplateProps, AuthorTemplateProps, NotFoundTemplateProps.

AuthorView

TS
interface AuthorView {
  id: string;
  displayName: string;
  title?: string;
  bio?: string;
  avatar?: MediaView;
  socials?: AuthorSocial[];
}

interface AuthorSocial {
  network: SocialNetwork;
  url: string;
}

CardPost

TS
type CardPost = Post & {
  url: string;                     // pre-computed by publisher
  hero?: MediaView;
  category?: { name: string; url: string };
  dateLabel?: string;              // pre-formatted, locale-aware
};

HomeTemplateProps.posts, CategoryTemplateProps.posts, AuthorTemplateProps.posts are all CardPost[].

Publisher types

PublishContext

TS
interface PublishContext {
  posts: Post[];
  pages: Post[];
  terms: Term[];
  media: Map<string, Media>;
  settings: SiteSettings;
  authors: Map<string, AuthorView>;
  // …
}

Action handlers receive (post, ctx: PublishContext). The context is already patched with the post-transition state (e.g. after post.deleted, ctx.posts no longer contains the deleted post).

PublishLogger

Used by regeneration target runners:

TS
interface PublishLogger {
  (entry: PublishLogEntry): void;
}

interface PublishLogEntry {
  level: "info" | "success" | "warn" | "error";
  message: string;
}
TS
api.registerRegenerationTarget({
  id: "my-plugin",
  labelKey: "regenerationTarget.label",
  descriptionKey: "regenerationTarget.description",
  run: async (ctx, log) => {
    log({ level: "info", message: "Starting…" });
    // …
    log({ level: "success", message: "Done." });
  },
});

Plugin types

PluginManifest

See Plugin manifest reference.

PluginApi

TS
interface PluginApi {
  addFilter: <T>(hook: string, fn, priority?: number) => void;
  addAction: (hook: string, fn, priority?: number) => void;
  registerBlock: (manifest: BlockManifest) => void;
  registerDashboardCard: (def: DashboardCardManifest) => void;
  registerRegenerationTarget: (def: RegenerationTarget) => void;
}

BlockManifest

See Plugin blocks and Theme blocks.

TS
interface BlockManifest {
  id: string;
  titleKey: string;
  namespace?: string;
  icon: ComponentType<{ size?: number }>;
  category: "text" | "media" | "layout" | "embed" | "advanced";
  insert: (chain, ctx) => void;
  isActive?: (editor) => boolean;
  extensions?: Extension[];
  inspector?: ComponentType<BlockInspectorProps>;
  nodeName?: string;
}

Image format types

TS
type ImageFit = "cover" | "contain";

interface ImageFormat {
  width: number;
  height: number;
  fit: ImageFit;
}

interface ImageFormatConfig {
  inputFormats: string[];          // e.g. [".jpg", ".jpeg", ".png", ".webp", ".gif"]
  outputFormat: "webp" | "jpeg" | "png";
  quality: number;                 // 1-100
  formats: Record<string, ImageFormat>;
  defaultFormat: string;
}

Continue