komal.nakrani
Browse Themes
ThemesCategoriesDocsBlogTips & Tricks
Astro Fundamentals••By Komal Nakrani

Astro TypeScript Types Cheatsheet

The Astro types you'll use most: Props, HTMLAttributes, Polymorphic, ComponentProps, CollectionEntry, GetStaticPaths, APIRoute, ImageMetadata and App. Locals.

#Astro#Cheatsheet#TypeScript

Astro types come from three places: astro (core types like APIRoute, GetStaticPaths and ImageMetadata), astro/types (component helpers like HTMLAttributes, Polymorphic and ComponentProps), and astro:content (CollectionEntry and friends). This cheatsheet lists the ones you’ll actually use, where to import each from, and a short example. Run npx astro check to type-check .astro files.

Key Takeaways

  • Declare interface Props in a component and Astro types Astro.props automatically.
  • HTMLAttributes<'a'> lets a wrapper accept every attribute of the element it renders.
  • CollectionEntry<'blog'> types a content entry; GetStaticPaths plus satisfies types routes.
  • Extend App.Locals in src/env.d.ts to type middleware data.

Component props

---
interface Props {
  title: string;
  href?: string;
  size?: 'sm' | 'md' | 'lg';
}
const { title, href, size = 'md' } = Astro.props;
---

type Props = { ... } works too. Astro reads either.

Wrapping HTML elements

---
import type { HTMLAttributes } from 'astro/types';

interface Props extends HTMLAttributes<'button'> {
  variant?: 'primary' | 'ghost';
}
const { variant = 'primary', ...attrs } = Astro.props;
---
<button class={`btn btn-${variant}`} {...attrs}><slot /></button>

Polymorphic components

---
import type { HTMLTag, Polymorphic } from 'astro/types';

type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }>;
const { as: Tag = 'div', ...props } = Astro.props;
---
<Tag {...props}><slot /></Tag>

<Box as="a" href="/"> is type-checked against <a> attributes.

Reusing another component’s props

import type { ComponentProps } from 'astro/types';
import Button from './Button.astro';

type ButtonProps = ComponentProps<typeof Button>;

Content collections

import type { CollectionEntry, CollectionKey } from 'astro:content';

type Post = CollectionEntry<'blog'>;
type Data = CollectionEntry<'blog'>['data'];

function sortByDate(posts: Post[]) {
  return posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
}

Routes

import type { GetStaticPaths, InferGetStaticPropsType, InferGetStaticParamsType } from 'astro';

export const getStaticPaths = (async () => {
  const posts = await getCollection('blog');
  return posts.map((post) => ({ params: { slug: post.id }, props: { post } }));
}) satisfies GetStaticPaths;

type Props = InferGetStaticPropsType<typeof getStaticPaths>;
type Params = InferGetStaticParamsType<typeof getStaticPaths>;
const { post } = Astro.props as Props;

Pagination

import type { Page } from 'astro';
interface Props { page: Page<CollectionEntry<'blog'>> }

Endpoints, middleware and context

import type { APIRoute, APIContext, MiddlewareHandler } from 'astro';

export const GET: APIRoute = ({ params, url }) => Response.json({ id: params.id });

export async function POST(context: APIContext) {
  return new Response(null, { status: 204 });
}

Middleware is usually typed through defineMiddleware() from astro:middleware, which infers the handler type.

Images

import type { ImageMetadata } from 'astro';

const images = import.meta.glob<{ default: ImageMetadata }>('../assets/*.jpg', { eager: true });

GetImageResult types the return value of getImage().

Markdown

import type { MarkdownHeading } from 'astro';
interface Props { headings: MarkdownHeading[] }   // { depth, slug, text }

Locals and sessions

// src/env.d.ts
declare namespace App {
  interface Locals {
    user: { id: string; name: string } | null;
  }
  interface SessionData {
    cart: string[];
  }
}

Astro.locals.user and Astro.session?.get('cart') are now typed.

Environment variables

astro:env modules are typed from env.schema automatically after npx astro sync. No manual declarations needed.

tsconfig

{
  "extends": "astro/tsconfigs/strict",
  "include": [".astro/types.d.ts", "**/*"],
  "exclude": ["dist"]
}

Presets: base, strict, strictest. strict is a good default for new projects.

Checking types

npx astro check     # type-check .astro, .ts and framework files
npx astro sync      # regenerate generated types

Add astro check to CI before the build. See the Astro CLI cheatsheet for both commands, the Astro components guide for props in practice, and the content collections cheatsheet for collection APIs. The TypeScript guide covers setup.

FAQ

Does Astro type-check during the build?

No. astro build doesn’t run the type checker. Run astro check separately, for example in CI.

Where do the generated types live?

In .astro/types.d.ts, generated by astro sync and the dev server. Include it in tsconfig.json.