Mastering TypeScript with Advanced Custom Fields
Advanced Custom Fields (ACF) is the go-to solution for flexible WordPress content. But in a TypeScript headless setup, losing type safety on your custom fields is a major pain point. This guide will show you every workaround the proper solution.

“TypeScript makes you feel like a wizard. ACF without types makes you feel like a detective. FlatWP gives you both.” – A satisfied developer
The Problem With Untyped ACF
When you query ACF fields through GraphQL, you get untyped data that looks like this:
const hero = page.acf.hero; // any type - no autocomplete, no safety
This means runtime errors, no IDE support, and constant trips to the WordPress admin to check field names. You can read more about this on the official ACF documentation.
Real-World Problems
Here are the issues we encountered before implementing type safety:
- Typos in field names that don’t show until production
- Refactoring field names becomes a search-and-replace nightmare
- No way to know what fields exist without checking WordPress admin
- Team members constantly asking “what’s the field name for X?”

Breaking It Down Further
The issue compounds when you have:
- Multiple developers on the team
- Complex field groups with nested repeaters
- Client-requested field changes mid-project
- Different ACF field types (relationships, galleries, etc.)
Nested List Example
Sometimes you need nested lists to organize information:
- Frontend Issues
- No autocomplete in VSCode/Cursor
- Runtime type errors
- Manual type guards everywhere
- Backend Issues
- Field name mismatches
- Data structure uncertainty
- Version control conflicts
Why h6 Headings Matter
Even though h6 is rarely used, it’s still valid HTML and should be styled properly. This is the deepest heading level you’ll encounter.
The FlatWP Solution
Our WordPress plugin exposes ACF schemas as structured JSON. Our codegen tool transforms these into TypeScript interfaces:
interface HeroBlock {
heading: string;
subheading: string;
image: {
url: string;
alt: string;
width: number;
height: number;
};
ctaText: string;
ctaUrl: string;
backgroundColor?: 'light' | 'dark' | 'accent';
}
Now your components are fully typed with intelligent autocomplete:
export function HeroBlock({ fields }: { fields: HeroBlock }) {
return (
<section className={`hero hero--${fields.backgroundColor || 'light'}`}>
<h1>{fields.heading}</h1>
<p>{fields.subheading}</p>
<img
src={fields.image.url}
alt={fields.image.alt}
width={fields.image.width}
height={fields.image.height}
/>
<a href={fields.ctaUrl}>{fields.ctaText}</a>
</section>
);
}

Advanced Formatting Examples
Let’s look at various ways to format content for maximum readability.
Mixed List Types
Here’s an ordered list with definitions:
- Generate Types – Run the codegen command after ACF changes
- Import Types – Use them in your component props
- Enjoy Safety – TypeScript catches errors before runtime
And an unordered list with code examples:
npm run generate-types– Generates ACF typesnpm run build– Builds the Next.js appnpm run dev– Runs development server
Pro tip: Add type generation to your pre-commit hooks so types are always in sync with your ACF configuration!
Complex Table Example
| ACF Field Type | TypeScript Type | GraphQL Return | Notes |
|---|---|---|---|
| Text | string | String | Simple text field |
| Number | number | Int/Float | Numeric values |
| True/False | boolean | Boolean | Checkbox value |
| Image | WPImage | MediaItem | Complex object with URL, alt, dimensions |
| Repeater | Array<T> | [Type] | Array of typed objects |
Flexible Content Blocks
ACF’s Flexible Content field type is perfect for page builders. FlatWP provides a block renderer pattern with full type safety:
const blockComponents = {
hero: HeroBlock,
features: FeaturesBlock,
testimonial: TestimonialBlock,
cta: CTABlock,
gallery: GalleryBlock,
} as const;
type BlockLayout = keyof typeof blockComponents;
interface ACFBlock<T = unknown> {
id: string;
layout: BlockLayout;
fields: T;
}
export function BlockRenderer({ blocks }: { blocks: ACFBlock[] }) {
return (
<>
{blocks.map((block) => {
const Component = blockComponents[block.layout];
if (!Component) {
console.warn(`Unknown block type: ${block.layout}`);
return null;
}
return <Component key={block.id} fields={block.fields} />;
})}
</>
);
}
Visit our documentation site for more examples and implementation details.
Installation Steps
Getting started is straightforward:
- Install the FlatWP WordPress plugin from the repository
- Configure your ACF field groups as usual
- Run
npm install @flatwp/typesin your Next.js project - Execute
npm run generate-typesto create TypeScript interfaces - Import and use types in your components
Important: Make sure to run type generation after every ACF schema change to keep your frontend in sync!

Coming in FlatWP Pro
The Pro version will include a library of 20+ pre-built ACF blocks with matching Shadcn components. Hero sections, feature grids, testimonials, pricing tables – all typed, styled, and ready to use.
Pro Feature Highlights
- Pre-configured ACF field groups
- Matching React/Shadcn components
- Dark mode variants
- Mobile-responsive layouts
- Accessibility built-in (ARIA labels, keyboard navigation)
- Animation variants using Framer Motion
You’ll be able to build complex page layouts in WordPress while maintaining full TypeScript safety in your React components. Check out our GitHub repository to follow development progress.
Conclusion: Type Safety Changed Everything
We shipped our first FlatWP site to production 6 months ago and haven’t looked back. Zero runtime errors related to ACF field access. Confident refactoring. Happy developers. Manual type checking Automatic type checking.
The investment in proper TypeScript integration pays dividends on every single project.