Static CMS provides first class support for Typescript when using the Bundling option.
Configuration
When using Typescript it is recommended to store your CMS configuration in a typescript file instead of a yaml file to take full advantage of the typings available.
import type { Config } from '@staticcms/core';
export const config: Config = {
...
}
Custom Widgets
When providing your own widgets, you can extend the Config
type with your custom widget types to provide proper typing in your config. All custom widget types should extend the BaseField
interface.
import type { Config, BaseField } from '@staticcms/core';
export interface HtmlField extends BaseField {
widget: 'html'
default: string;
}
export interface CustomField extends BaseField {
widget: 'custom'
default: number[];
some_other_prop: string;
}
export const config: Config<HtmlField | CustomField> = {
...
}
Widgets
When providing types for custom widgets you need to know the datatype your widget works with and the definition of its Field
. The examples below assumes the field interface for the widget is in the config.ts
file in the parent directory.
Control Component
Control widgets take the WidgetControlProps
interface as their props.
import type { FC } from 'react';
import type { WidgetControlProps } from '@staticcms/core';
import type { HtmlField } from '../config';
const EditorControl: FC<WidgetControlProps<string, HtmlField>> = ({
field, // Typed as a HtmlField
value, // Typed as string | null | undefined
onChange, // Typed to accept string | null | undefined
}) => {
...
};
Preview Component
Control widgets take the WidgetPreviewProps
interface as their props.
import type { FC } from 'react';
import type { WidgetPreviewProps } from '@staticcms/core';
import type { HtmlField } from '../config';
const EditorPreview: FC<WidgetPreviewProps<string, HtmlField>> = ({
field, // Typed as a HtmlField
value, // Typed as string | null | undefined
}) => {
...
};
Preview Templates
When providing types for custom preview templates you need to know the datatype of your collection (or file) that the template is represents. Preview templates take the TemplatePreviewProps
interface as their props.
import { useEffect, useMemo, useState } from 'react';
import type { FC } from 'react';
import type { TemplatePreviewProps } from '@staticcms/core';
interface PostPreviewData {
body: string;
date: string;
title: string;
image?: string;
slug: string;
tags?: string[];
}
const PostPreview: FC<TemplatePreviewProps<PostPreviewData>> = ({
entry, // Typed as PostPreviewData
widgetFor, // Typed to accept only the keys of PostPreviewData (body, date, title, image, slug, tags)
collection,
fields
}) => {
const dateString = useMemo(() => entry.data.date, [entry.data.date]);
const imageField = useMemo(() =>
fields.find((field) => field.name === 'image'),
[fields]
);
const image = useMediaAsset(entry.data.image ?? '', collection, imageField, entry);
return (
<div>
<h1>{entry.data.title}</h1>
<div>{entry.data.date}</div>
<img title={title} src={image} />
<div>{(entry.data.tags ?? []).join(', ')}</div>
<div>{widgetFor('body')}</div>
</div>
);
};