Field Forge auto-generates TypeScript type definitions (.d.ts files) for every field group on your WordPress site. Every field type is mapped to its TypeScript equivalent. Repeater fields become Array<SubFields>. Flexible content becomes discriminated unions. The result: a fully-typed custom fields API for your Next.js, Astro, Nuxt, SvelteKit, or any JavaScript frontend.
Headless WordPress means WordPress serves as the CMS and a separate JavaScript frontend (Next.js, Astro, Nuxt, etc.) renders the pages. The frontend fetches content via REST API or WPGraphQL and displays it. For simple content — title, excerpt, featured image — TypeScript types are built into most SDKs.
Custom fields are the problem. Custom fields are plugin-defined, not core WordPress. TypeScript doesn’t know that your “Hero Section” field group has a title: string, subtitle: string, background_image: WPImage, and cta_button: { text: string; url: string; }. Frontend code that accesses data.hero_title has no type safety — typos, missing fields, and field-rename refactors all become runtime errors.
The solutions that existed before Field Forge:
any — defeats the purpose of TypeScriptField Forge provides a simpler path: auto-generate TypeScript definitions directly from your field groups.
You design a field group in the visual builder — let’s say “Hero Section” with fields for title, subtitle, background image, and CTA button.
In the field group admin screen, Field Forge has a “Download TypeScript” button that generates a .d.ts file containing the type definitions for all your field groups.
Commit the .d.ts file to your frontend repo (or copy-paste into an existing types file). TypeScript now knows the exact shape of every field group’s data.
import { HeroSectionFields } from './types/fieldforge.d.ts';
function Hero({ data }: { data: HeroSectionFields }) {
return (
<section>
<h1>{data.title}</h1>
<p>{data.subtitle}</p>
<img src={data.background_image.url} alt={data.background_image.alt} />
<a href={data.cta_button.url}>{data.cta_button.text}</a>
</section>
);
}
TypeScript catches typos, missing fields, and wrong types at compile time. IDE autocomplete works. Refactoring is safe.
Every Field Forge field type has a corresponding TypeScript type:
| Field Forge field | TypeScript type |
|---|---|
| Text | string |
| Textarea | string |
| Number | number |
| Range | number |
string |
|
| URL | string |
| Password | string (sensitive) |
| Image | WPImage (built-in interface) |
| File | WPFile |
| WYSIWYG | string (HTML) |
| oEmbed | string (embed HTML) |
| Gallery | WPImage[] |
| Select (single) | 'option_a' \| 'option_b' \| 'option_c' (literal union of choices) |
| Select (multi) | Array<'option_a' \| 'option_b'> |
| Checkbox | string[] |
| Radio | 'option_a' \| 'option_b' |
| True/False | boolean |
| Button Group | 'option_a' \| 'option_b' |
| Relationship | WPPost[] |
| Post Object | WPPost |
| Page Link | string (URL) |
| Taxonomy | WPTerm[] or WPTerm (depending on settings) |
| User | WPUser or WPUser[] |
| Date Picker | string (ISO format) |
| Time Picker | string |
| Color Picker | string (hex or rgba) |
| Repeater | Array<RepeaterSubFields> (nested type) |
| Group | nested type |
| Flexible Content | Array<LayoutA \| LayoutB \| LayoutC> (discriminated union) |
| Clone | reference to cloned group |
Field Forge’s generated .d.ts includes helper types you’ll use throughout your frontend:
interface WPImage {
id: number;
url: string;
alt: string;
width: number;
height: number;
sizes: {
thumbnail: string;
medium: string;
large: string;
full: string;
};
}
interface WPFile {
id: number;
url: string;
filename: string;
mime_type: string;
filesize: number;
}
interface WPPost {
id: number;
title: string;
slug: string;
content: string;
excerpt: string;
date: string;
modified: string;
featured_image: WPImage | null;
categories: WPTerm[];
tags: WPTerm[];
}
interface WPUser {
id: number;
name: string;
email: string;
avatar: string;
}
interface WPTerm {
id: number;
name: string;
slug: string;
description: string;
}
A field group called “Hero Section” with title, subtitle, background image, and CTA button generates:
export interface HeroSectionFields {
title: string;
subtitle: string;
background_image: WPImage;
cta_button: {
text: string;
url: string;
open_in_new_tab: boolean;
};
}
Usage in a Next.js component:
import type { HeroSectionFields } from '@/types/fieldforge';
interface Props {
hero: HeroSectionFields;
}
export function Hero({ hero }: Props) {
return (
<section className="hero">
<img
src={hero.background_image.sizes.large}
alt={hero.background_image.alt}
width={hero.background_image.width}
height={hero.background_image.height}
/>
<h1>{hero.title}</h1>
<p>{hero.subtitle}</p>
<a
href={hero.cta_button.url}
target={hero.cta_button.open_in_new_tab ? '_blank' : undefined}
>
{hero.cta_button.text}
</a>
</section>
);
}
Every property is typed. TypeScript catches hero.titel (typo), hero.background_image.URL (wrong case), and hero.cta_button.open_new_tab (wrong field name) at compile time.
Repeater fields become Array<SubFields>. Nested repeaters work recursively:
export interface TeamPageFields {
team_members: Array<{
name: string;
photo: WPImage;
bio: string;
skills: Array<{
skill_name: string;
proficiency: number;
}>;
}>;
}
TypeScript understands the nesting. Iterating over team_members and then skills is fully typed.
Flexible content fields generate discriminated unions — TypeScript’s native feature for handling multi-type arrays:
type HeroLayout = {
acf_fc_layout: 'hero';
title: string;
subtitle: string;
background_image: WPImage;
};
type FeaturesLayout = {
acf_fc_layout: 'features';
section_title: string;
features: Array<{
icon: string;
title: string;
description: string;
}>;
};
type CTALayout = {
acf_fc_layout: 'cta';
headline: string;
button: { text: string; url: string };
};
export interface LandingPageFields {
page_sections: Array<HeroLayout | FeaturesLayout | CTALayout>;
}
In your frontend component, use a type guard to render the right layout:
function PageSections({ sections }: { sections: LandingPageFields['page_sections'] }) {
return (
<>
{sections.map((section, i) => {
if (section.acf_fc_layout === 'hero') {
return <Hero key={i} data={section} />;
}
if (section.acf_fc_layout === 'features') {
return <Features key={i} data={section} />;
}
if (section.acf_fc_layout === 'cta') {
return <CTA key={i} data={section} />;
}
})}
</>
);
}
Inside each if block, TypeScript narrows section to the correct type. No casting needed.
When you edit field groups in Field Forge’s admin, the types change. To keep your frontend in sync:
.d.ts file in your frontend repoOr automate it via Field Forge’s REST API:
# Fetch the latest TypeScript definitions via curl
curl https://wp.example.com/wp-json/fieldforge/v1/typescript > types/fieldforge.d.ts
Add this to your frontend build pipeline and types are always up to date.
TypeScript types are framework-agnostic. Field Forge’s generated types work with:
Get Field Forge — from $35/year →
TypeScript generation is included in every paid plan. No other WordPress custom fields plugin has this feature.