> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/preactjs/preact/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript

> Use Preact with TypeScript for type safety and better developer experience

Preact has first-class TypeScript support with complete type definitions included in the core package. No additional `@types` packages are needed.

## Setup

Preact's TypeScript definitions are included by default. Just install Preact and start using TypeScript:

<Steps>
  ### Install Preact

  ```bash theme={null}
  npm install preact
  ```

  ### Configure TypeScript

  Create or update your `tsconfig.json`:

  ```json tsconfig.json theme={null}
  {
    "compilerOptions": {
      "target": "ES2020",
      "module": "ESNext",
      "jsx": "react-jsx",
      "jsxImportSource": "preact",
      "moduleResolution": "bundler",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true,
      "lib": ["ES2020", "DOM", "DOM.Iterable"]
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
  }
  ```

  ### Start coding

  Create a `.tsx` file and start building:

  ```tsx App.tsx theme={null}
  import { render } from 'preact';

  function App() {
    return <h1>Hello TypeScript!</h1>;
  }

  render(<App />, document.getElementById('app')!);
  ```
</Steps>

## Type Definitions

Preact's type definitions are located in the source code:

* `src/index.d.ts` - Core Preact types
* `src/jsx.d.ts` - JSX type definitions
* `hooks/src/index.d.ts` - Hooks type definitions
* `compat/src/index.d.ts` - React compatibility types

Reference: `src/index.d.ts`, `hooks/src/index.d.ts`

## Component Types

<Tabs>
  <Tab title="Function Components">
    Function components are typed using the `FunctionComponent` type:

    ```tsx theme={null}
    import { FunctionComponent } from 'preact';

    interface Props {
      name: string;
      age?: number;
    }

    const Greeting: FunctionComponent<Props> = ({ name, age }) => {
      return (
        <div>
          <h1>Hello, {name}!</h1>
          {age && <p>Age: {age}</p>}
        </div>
      );
    };
    ```

    Or use the shorthand (recommended):

    ```tsx theme={null}
    interface Props {
      name: string;
      age?: number;
    }

    function Greeting({ name, age }: Props) {
      return (
        <div>
          <h1>Hello, {name}!</h1>
          {age && <p>Age: {age}</p>}
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="Class Components">
    Class components extend `Component` with props and state types:

    ```tsx theme={null}
    import { Component } from 'preact';

    interface Props {
      initialCount: number;
    }

    interface State {
      count: number;
    }

    class Counter extends Component<Props, State> {
      state: State = {
        count: this.props.initialCount
      };

      increment = () => {
        this.setState({ count: this.state.count + 1 });
      };

      render() {
        return (
          <div>
            <p>Count: {this.state.count}</p>
            <button onClick={this.increment}>Increment</button>
          </div>
        );
      }
    }
    ```
  </Tab>
</Tabs>

## Props with Children

The `ComponentChildren` type represents valid children:

```tsx theme={null}
import { ComponentChildren } from 'preact';

interface Props {
  title: string;
  children: ComponentChildren;
}

function Card({ title, children }: Props) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <div>{children}</div>
    </div>
  );
}
```

Alternatively, use `FunctionComponent` which includes children automatically:

```tsx theme={null}
import { FunctionComponent } from 'preact';

interface Props {
  title: string;
}

// children is available automatically
const Card: FunctionComponent<Props> = ({ title, children }) => (
  <div className="card">
    <h2>{title}</h2>
    <div>{children}</div>
  </div>
);
```

## Hooks with TypeScript

<CodeGroup>
  ```tsx useState theme={null}
  import { useState } from 'preact/hooks';

  // Type is inferred
  function Counter() {
    const [count, setCount] = useState(0);
    // count: number, setCount: (value: number) => void
    
    return (
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
    );
  }

  // Explicit type
  interface User {
    name: string;
    email: string;
  }

  function UserProfile() {
    const [user, setUser] = useState<User | null>(null);
    
    return (
      <div>
        {user ? <p>{user.name}</p> : <p>Loading...</p>}
      </div>
    );
  }
  ```

  ```tsx useReducer theme={null}
  import { useReducer } from 'preact/hooks';

  interface State {
    count: number;
    error: string | null;
  }

  type Action =
    | { type: 'increment' }
    | { type: 'decrement' }
    | { type: 'reset'; payload: number }
    | { type: 'error'; payload: string };

  function reducer(state: State, action: Action): State {
    switch (action.type) {
      case 'increment':
        return { ...state, count: state.count + 1 };
      case 'decrement':
        return { ...state, count: state.count - 1 };
      case 'reset':
        return { ...state, count: action.payload };
      case 'error':
        return { ...state, error: action.payload };
      default:
        return state;
    }
  }

  function Counter() {
    const [state, dispatch] = useReducer(reducer, {
      count: 0,
      error: null
    });

    return (
      <div>
        <p>Count: {state.count}</p>
        <button onClick={() => dispatch({ type: 'increment' })}>
          +
        </button>
        <button onClick={() => dispatch({ type: 'reset', payload: 0 })}>
          Reset
        </button>
      </div>
    );
  }
  ```

  ```tsx useRef theme={null}
  import { useRef } from 'preact/hooks';

  // DOM element ref
  function TextInput() {
    const inputRef = useRef<HTMLInputElement>(null);

    const focusInput = () => {
      inputRef.current?.focus();
    };

    return (
      <div>
        <input ref={inputRef} type="text" />
        <button onClick={focusInput}>Focus</button>
      </div>
    );
  }

  // Mutable value ref
  function Timer() {
    const intervalRef = useRef<number | null>(null);

    const start = () => {
      intervalRef.current = window.setInterval(() => {
        console.log('tick');
      }, 1000);
    };

    const stop = () => {
      if (intervalRef.current !== null) {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
      }
    };

    return (
      <div>
        <button onClick={start}>Start</button>
        <button onClick={stop}>Stop</button>
      </div>
    );
  }
  ```

  ```tsx useContext theme={null}
  import { createContext } from 'preact';
  import { useContext } from 'preact/hooks';

  interface Theme {
    primary: string;
    secondary: string;
  }

  const ThemeContext = createContext<Theme>({
    primary: '#007bff',
    secondary: '#6c757d'
  });

  function ThemedButton() {
    const theme = useContext(ThemeContext);
    // theme: Theme
    
    return (
      <button style={{ backgroundColor: theme.primary }}>
        Click me
      </button>
    );
  }

  // With undefined default (requires checking)
  const AuthContext = createContext<User | undefined>(undefined);

  function useAuth() {
    const context = useContext(AuthContext);
    if (!context) {
      throw new Error('useAuth must be used within AuthProvider');
    }
    return context;
  }
  ```
</CodeGroup>

## Event Handlers

Preact provides specific event types for all DOM events:

```tsx theme={null}
import { JSX } from 'preact';

function Form() {
  const handleSubmit = (e: JSX.TargetedEvent<HTMLFormElement, Event>) => {
    e.preventDefault();
    console.log('Form submitted');
  };

  const handleChange = (e: JSX.TargetedEvent<HTMLInputElement, Event>) => {
    console.log('Input value:', e.currentTarget.value);
  };

  const handleClick = (e: JSX.TargetedMouseEvent<HTMLButtonElement>) => {
    console.log('Button clicked at', e.clientX, e.clientY);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" onChange={handleChange} />
      <button onClick={handleClick}>Submit</button>
    </form>
  );
}
```

Common event types:

* `JSX.TargetedEvent<T, E>` - Generic event
* `JSX.TargetedMouseEvent<T>` - Mouse events
* `JSX.TargetedKeyboardEvent<T>` - Keyboard events
* `JSX.TargetedFocusEvent<T>` - Focus events

## Refs with TypeScript

<CodeGroup>
  ```tsx Component Refs theme={null}
  import { Component } from 'preact';
  import { createRef } from 'preact';

  class VideoPlayer extends Component {
    play() {
      console.log('Playing...');
    }

    pause() {
      console.log('Paused');
    }
  }

  class VideoController extends Component {
    playerRef = createRef<VideoPlayer>();

    handlePlay = () => {
      this.playerRef.current?.play();
    };

    handlePause = () => {
      this.playerRef.current?.pause();
    };

    render() {
      return (
        <div>
          <VideoPlayer ref={this.playerRef} />
          <button onClick={this.handlePlay}>Play</button>
          <button onClick={this.handlePause}>Pause</button>
        </div>
      );
    }
  }
  ```

  ```tsx Forward Refs theme={null}
  import { forwardRef } from 'preact/compat';
  import { Ref } from 'preact';

  interface Props {
    label: string;
  }

  const TextInput = forwardRef<HTMLInputElement, Props>(
    ({ label }, ref) => (
      <div>
        <label>{label}</label>
        <input ref={ref} type="text" />
      </div>
    )
  );

  // Usage
  function Form() {
    const inputRef = useRef<HTMLInputElement>(null);

    return (
      <form>
        <TextInput ref={inputRef} label="Name" />
        <button onClick={() => inputRef.current?.focus()}>
          Focus Input
        </button>
      </form>
    );
  }
  ```

  ```tsx Callback Refs theme={null}
  function Component() {
    const handleRef = (element: HTMLDivElement | null) => {
      if (element) {
        console.log('Element mounted:', element);
      } else {
        console.log('Element unmounted');
      }
    };

    return <div ref={handleRef}>Content</div>;
  }
  ```
</CodeGroup>

## Context with TypeScript

```tsx theme={null}
import { createContext } from 'preact';
import { useContext, useState } from 'preact/hooks';

interface User {
  id: string;
  name: string;
  email: string;
}

interface AuthContextValue {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}

const AuthContext = createContext<AuthContextValue | undefined>(undefined);

export function AuthProvider({ children }: { children: ComponentChildren }) {
  const [user, setUser] = useState<User | null>(null);

  const login = async (email: string, password: string) => {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ email, password })
    });
    const userData = await response.json();
    setUser(userData);
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
}
```

## Custom Hooks

Type your custom hooks like regular functions:

```tsx theme={null}
import { useState, useEffect } from 'preact/hooks';

interface FetchState<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
}

function useFetch<T>(url: string): FetchState<T> {
  const [state, setState] = useState<FetchState<T>>({
    data: null,
    loading: true,
    error: null
  });

  useEffect(() => {
    let cancelled = false;

    fetch(url)
      .then(res => res.json())
      .then(data => {
        if (!cancelled) {
          setState({ data, loading: false, error: null });
        }
      })
      .catch(error => {
        if (!cancelled) {
          setState({ data: null, loading: false, error });
        }
      });

    return () => {
      cancelled = true;
    };
  }, [url]);

  return state;
}

// Usage
interface User {
  id: number;
  name: string;
}

function UserProfile({ userId }: { userId: number }) {
  const { data, loading, error } = useFetch<User>(
    `/api/users/${userId}`
  );

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!data) return <div>No data</div>;

  return <div>{data.name}</div>;
}
```

## Generic Components

Create reusable components that work with any type:

```tsx theme={null}
import { ComponentChildren } from 'preact';

interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => ComponentChildren;
  keyExtractor: (item: T) => string | number;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map(item => (
        <li key={keyExtractor(item)}>
          {renderItem(item)}
        </li>
      ))}
    </ul>
  );
}

// Usage
interface User {
  id: number;
  name: string;
}

const users: User[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

function App() {
  return (
    <List
      items={users}
      renderItem={user => <span>{user.name}</span>}
      keyExtractor={user => user.id}
    />
  );
}
```

## Utility Types

Preact exports useful utility types:

```tsx theme={null}
import {
  ComponentChildren,
  ComponentType,
  VNode,
  RefObject,
  ComponentProps
} from 'preact';

// Get props type from a component
const Button: FunctionComponent<{ label: string }> = ({ label }) => (
  <button>{label}</button>
);

type ButtonProps = ComponentProps<typeof Button>;
// { label: string; children?: ComponentChildren }

// RefObject type
const ref: RefObject<HTMLDivElement> = { current: null };

// VNode type
const vnode: VNode = <div>Hello</div>;
```

## Best Practices

<Steps>
  ### Enable strict mode

  ```json tsconfig.json theme={null}
  {
    "compilerOptions": {
      "strict": true,
      "strictNullChecks": true,
      "noImplicitAny": true
    }
  }
  ```

  ### Use inference when possible

  ```tsx theme={null}
  // Good: Type is inferred
  const [count, setCount] = useState(0);

  // Unnecessary: Type already inferred
  const [count, setCount] = useState<number>(0);

  // Necessary: Complex type or null initial value
  const [user, setUser] = useState<User | null>(null);
  ```

  ### Create interfaces for props

  ```tsx theme={null}
  // Good: Reusable interface
  interface ButtonProps {
    label: string;
    onClick: () => void;
    variant?: 'primary' | 'secondary';
  }

  function Button({ label, onClick, variant = 'primary' }: ButtonProps) {
    return (
      <button className={variant} onClick={onClick}>
        {label}
      </button>
    );
  }
  ```

  ### Use discriminated unions for state

  ```tsx theme={null}
  type AsyncState<T> =
    | { status: 'idle' }
    | { status: 'loading' }
    | { status: 'success'; data: T }
    | { status: 'error'; error: Error };

  function UserProfile() {
    const [state, setState] = useState<AsyncState<User>>({ 
      status: 'idle' 
    });

    if (state.status === 'loading') return <div>Loading...</div>;
    if (state.status === 'error') return <div>{state.error.message}</div>;
    if (state.status === 'success') return <div>{state.data.name}</div>;
    return null;
  }
  ```
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Hooks" icon="hook" href="/guides/working-with-hooks">
    Learn about hooks with full TypeScript support
  </Card>

  <Card title="Context" icon="share-nodes" href="/guides/context">
    Type-safe context with TypeScript
  </Card>
</CardGroup>
