Preact is renowned for its lightweight nature and efficient virtual DOM implementation, but building applications still requires performance optimization. By adopting best practices and strategies, you can further enhance the performance of Preact applications.
Code Splitting and Lazy Loading
Dynamic Imports
Using dynamic import() statements enables code splitting, loading modules only when needed.
import { lazy } from 'preact/compat';
const Button = lazy(() => import('./Button'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Button />
</Suspense>
);
}Route-Based Lazy Loading
Lazy loading at the route level can significantly reduce initial load times.
import { lazy, Suspense } from 'preact/compat';
import { Router, Route } from 'preact-router';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Router>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</Router>
</Suspense>
);
}Reducing Render Frequency
Using shouldComponentUpdate
In class components, override shouldComponentUpdate to control whether a component should re-render.
import { Component } from 'preact';
class MyComponent extends Component {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.someProp !== this.props.someProp || nextState.someState !== this.state.someState;
}
render() {
// ...
}
}Using React.memo
For function components, use React.memo to prevent unnecessary renders.
import { memo } from 'preact/compat';
const MyComponent = memo(function MyComponent(props) {
// ...
});Using useMemo and useCallback
These Hooks cache computation results and functions, avoiding recomputation or new function instances on every render.
import { useMemo, useCallback } from 'preact/hooks';
function MyComponent({ someProp }) {
const memoizedValue = useMemo(() => computeExpensiveValue(someProp), [someProp]);
const memoizedFunction = useCallback(() => doSomething(), []);
// ...
}Optimizing State Management
Using useReducer Instead of useState
For complex state logic, useReducer offers better performance and maintainability.
import { useReducer } from 'preact/hooks';
function myReducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(myReducer, initialState);
// ...
}Using the Context API
Avoid deep prop drilling by using the Context API to share state and callbacks.
import { createContext, useContext } from 'preact';
import { useState } from 'preact/hooks';
const MyContext = createContext();
function MyProvider({ children }) {
const [state, setState] = useState(initialState);
return (
<MyContext.Provider value={{ state, setState }}>
{children}
</MyContext.Provider>
);
}
function MyComponent() {
const { state, setState } = useContext(MyContext);
// ...
}Building for Production
Ensure you use Preact’s production build for deployment, which removes warnings and debugging information to reduce bundle size.
"scripts": {
"build": "preact build --env production"
}Summary
Optimizing Preact performance involves multiple strategies, from code splitting and lazy loading to reducing render frequency and refining state management. These techniques can significantly improve application performance, delivering faster load times and smoother user experiences. By leveraging Preact’s tools and best practices—such as shouldComponentUpdate, React.memo, useMemo, and useCallback—you can effectively minimize unnecessary renders and boost responsiveness. This tutorial equips you with the core techniques for optimizing Preact performance, laying a solid foundation for building high-performance web applications.



