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.
This guide will walk you through creating your first Preact application step by step. By the end, you’ll have a working interactive component that demonstrates Preact’s core features.
Prerequisites
Before you begin, make sure you have:
Node.js 20.0 or later installed
A package manager (npm, yarn, or pnpm)
A text editor or IDE
Basic knowledge of JavaScript and HTML
Create your first app
Set up your project
Create a new directory and initialize a package.json file: mkdir my-preact-app
cd my-preact-app
npm init -y
Install Preact:
Create an HTML file
Create an index.html file as your entry point: <! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > My Preact App </ title >
</ head >
< body >
< div id = "app" ></ div >
< script type = "module" src = "/index.js" ></ script >
</ body >
</ html >
Create your first component
Create an index.js file with a simple component: import { h , render } from 'preact' ;
const App = () => {
return (
< div >
< h1 > Hello from Preact! </ h1 >
< p > This is your first component. </ p >
</ div >
);
};
render ( < App /> , document . getElementById ( 'app' ));
Note: To use JSX syntax, you’ll need a build tool. For now, you can use the h function directly: import { h , render } from 'preact' ;
const App = () => {
return h ( 'div' , null ,
h ( 'h1' , null , 'Hello from Preact!' ),
h ( 'p' , null , 'This is your first component.' )
);
};
render ( h ( App ), document . getElementById ( 'app' ));
Add state with hooks
Make your component interactive by adding state with the useState hook: import { h , render } from 'preact' ;
import { useState } from 'preact/hooks' ;
const Counter = () => {
const [ count , setCount ] = useState ( 0 );
return (
< div >
< h1 > Counter: { count } </ h1 >
< button onClick = { () => setCount ( count + 1 ) } >
Increment
</ button >
< button onClick = { () => setCount ( count - 1 ) } >
Decrement
</ button >
< button onClick = { () => setCount ( 0 ) } >
Reset
</ button >
</ div >
);
};
render ( < Counter /> , document . getElementById ( 'app' ));
Without JSX: import { h , render } from 'preact' ;
import { useState } from 'preact/hooks' ;
const Counter = () => {
const [ count , setCount ] = useState ( 0 );
return h ( 'div' , null ,
h ( 'h1' , null , `Counter: ${ count } ` ),
h ( 'button' , { onClick : () => setCount ( count + 1 ) }, 'Increment' ),
h ( 'button' , { onClick : () => setCount ( count - 1 ) }, 'Decrement' ),
h ( 'button' , { onClick : () => setCount ( 0 ) }, 'Reset' )
);
};
render ( h ( Counter ), document . getElementById ( 'app' ));
Add effects
Use the useEffect hook to perform side effects: import { h , render } from 'preact' ;
import { useState , useEffect } from 'preact/hooks' ;
const App = () => {
const [ count , setCount ] = useState ( 0 );
useEffect (() => {
document . title = `Count: ${ count } ` ;
}, [ count ]);
return (
< div >
< h1 > Counter: { count } </ h1 >
< button onClick = { () => setCount ( count + 1 ) } >
Increment
</ button >
< p > The page title updates with the count! </ p >
</ div >
);
};
render ( < App /> , document . getElementById ( 'app' ));
Build with components
Break your UI into reusable components: import { h , render } from 'preact' ;
import { useState } from 'preact/hooks' ;
const Button = ({ onClick , children }) => (
< button
onClick = { onClick }
style = { { padding: '10px' , margin: '5px' } }
>
{ children }
</ button >
);
const Display = ({ value }) => (
< div style = { { fontSize: '2em' , margin: '20px' } } >
Count: { value }
</ div >
);
const App = () => {
const [ count , setCount ] = useState ( 0 );
return (
< div >
< h1 > Component-based Counter </ h1 >
< Display value = { count } />
< div >
< Button onClick = { () => setCount ( count + 1 ) } >
+
</ Button >
< Button onClick = { () => setCount ( count - 1 ) } >
-
</ Button >
< Button onClick = { () => setCount ( 0 ) } >
Reset
</ Button >
</ div >
</ div >
);
};
render ( < App /> , document . getElementById ( 'app' ));
Working with class components
While functional components with hooks are recommended, Preact also supports class components:
import { Component , render } from 'preact' ;
class Counter extends Component {
state = { count: 0 };
increment = () => {
this . setState ({ count: this . state . count + 1 });
};
decrement = () => {
this . setState ({ count: this . state . count - 1 });
};
render () {
return (
< div >
< h1 > Counter: { this . state . count } </ h1 >
< button onClick = { this . increment } > Increment </ button >
< button onClick = { this . decrement } > Decrement </ button >
</ div >
);
}
}
render ( < Counter /> , document . getElementById ( 'app' ));
Component lifecycle
Class components have lifecycle methods for different stages:
import { Component , render } from 'preact' ;
class LifecycleDemo extends Component {
state = { data: null };
componentDidMount () {
// Called after component is mounted
console . log ( 'Component mounted' );
this . fetchData ();
}
componentDidUpdate ( prevProps , prevState ) {
// Called after component updates
console . log ( 'Component updated' );
}
componentWillUnmount () {
// Called before component is unmounted
console . log ( 'Component will unmount' );
}
fetchData = async () => {
const response = await fetch ( 'https://api.example.com/data' );
const data = await response . json ();
this . setState ({ data });
};
render () {
return (
< div >
{ this . state . data ? (
< pre > { JSON . stringify ( this . state . data , null , 2 ) } </ pre >
) : (
< p > Loading... </ p >
) }
</ div >
);
}
}
render ( < LifecycleDemo /> , document . getElementById ( 'app' ));
With functional components, use hooks instead:
import { h , render } from 'preact' ;
import { useState , useEffect } from 'preact/hooks' ;
const LifecycleDemo = () => {
const [ data , setData ] = useState ( null );
useEffect (() => {
// Runs after mount and updates
console . log ( 'Component mounted or updated' );
const fetchData = async () => {
const response = await fetch ( 'https://api.example.com/data' );
const data = await response . json ();
setData ( data );
};
fetchData ();
// Cleanup function (runs before unmount)
return () => {
console . log ( 'Component will unmount' );
};
}, []); // Empty array = run only on mount
return (
< div >
{ data ? (
< pre > { JSON . stringify ( data , null , 2 ) } </ pre >
) : (
< p > Loading... </ p >
) }
</ div >
);
};
render ( < LifecycleDemo /> , document . getElementById ( 'app' ));
Complete example
Here’s a complete todo app that demonstrates multiple concepts:
import { h , render } from 'preact' ;
import { useState } from 'preact/hooks' ;
const TodoItem = ({ todo , onToggle , onDelete }) => (
< li style = { { margin: '10px 0' } } >
< label >
< input
type = "checkbox"
checked = { todo . completed }
onChange = { () => onToggle ( todo . id ) }
/>
< span style = { {
textDecoration: todo . completed ? 'line-through' : 'none' ,
marginLeft: '10px'
} } >
{ todo . text }
</ span >
</ label >
< button
onClick = { () => onDelete ( todo . id ) }
style = { { marginLeft: '10px' } }
>
Delete
</ button >
</ li >
);
const TodoApp = () => {
const [ todos , setTodos ] = useState ([]);
const [ input , setInput ] = useState ( '' );
const addTodo = ( e ) => {
e . preventDefault ();
if ( ! input . trim ()) return ;
setTodos ([
... todos ,
{
id: Date . now (),
text: input ,
completed: false
}
]);
setInput ( '' );
};
const toggleTodo = ( id ) => {
setTodos ( todos . map ( todo =>
todo . id === id ? { ... todo , completed: ! todo . completed } : todo
));
};
const deleteTodo = ( id ) => {
setTodos ( todos . filter ( todo => todo . id !== id ));
};
return (
< div >
< h1 > Todo List </ h1 >
< form onSubmit = { addTodo } >
< input
type = "text"
value = { input }
onInput = { ( e ) => setInput ( e . target . value ) }
placeholder = "What needs to be done?"
style = { { padding: '8px' , fontSize: '16px' } }
/>
< button type = "submit" style = { { padding: '8px 16px' , marginLeft: '10px' } } >
Add
</ button >
</ form >
< ul style = { { listStyle: 'none' , padding: 0 } } >
{ todos . map ( todo => (
< TodoItem
key = { todo . id }
todo = { todo }
onToggle = { toggleTodo }
onDelete = { deleteTodo }
/>
)) }
</ ul >
< p >
{ todos . filter ( t => ! t . completed ). length } of { todos . length } items remaining
</ p >
</ div >
);
};
render ( < TodoApp /> , document . getElementById ( 'app' ));
Key concepts recap
Render function Use render() to mount components to the DOM. It accepts a component and a parent DOM element.
useState hook Add state to functional components. Returns current state and a setter function.
useEffect hook Perform side effects like data fetching, subscriptions, or DOM manipulation.
Component composition Break your UI into small, reusable components that accept props and return JSX.
Next steps
Now that you’ve built your first Preact app, explore more features:
Learn about hooks Dive deeper into hooks for state management and side effects
Explore the API Browse the complete Preact API documentation
Server-side rendering Learn how to render Preact components on the server
React compatibility Use React libraries with preact/compat