In this article, we will discuss a quick way to deploy a Preact application and run it in the cloud environment. We will go step by step through all the stages, from the initial project setup to cloud deployment.
Preact is a lightweight alternative to React. Preact fully supports JSX, familiar hooks, and most ecosystem solutions (routers, state management libraries). The minified build of Preact is only about 3 KB, whereas React is significantly heavier. This is critical when load speed and interface responsiveness are important.
To understand the benefits for developers, let's compare Preact and React in a small table:
Parameter |
Preact |
React |
Library size |
~3 KB |
~30–40 KB |
JSX compatibility |
Full support |
Full support |
Hooks support |
Yes |
Yes |
Ecosystem |
Relatively small but growing |
Extensive, many ready-made solutions |
Performance |
High, optimized for minimal size |
High, but requires additional optimization |
Preact is great for projects where speed and small frontend size are important. If you want to quickly launch an MVP or a lightweight prototype, Preact will solve the problem more easily and efficiently. Moreover, developers familiar with React can easily switch to Preact thanks to its similar API.
To start local development, you need to install the latest version of Node.js (we recommend v22.14.0 LTS). Along with Node.js, npm — the standard JavaScript package manager — will be installed.
For Windows:
Go to the official website nodejs.org and download the installer.
Run the installer and follow the instructions.
For Linux/MacOS (based on Unix kernel):
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
\. "$HOME/.nvm/nvm.sh"
After installing nvm, install Node.js 22:
nvm install 22
After installation, run the following commands in the terminal to check the versions:
node -v && npm -v
Make sure that the versions of Node.js and npm are displayed correctly.
To quickly generate the project skeleton, use the Vite tool — a modern tool for building front-end applications. When creating your application, choose the Preact template:
npm create vite@latest my-project -- --template preact
Go to the directory of the newly created project and install the dependencies:
cd my-project
npm install
To start the development build, use:
npm run dev
The project will run on the default port 5173
.
If you need to use a different port, use:
npm run dev -- --port 3000
If you prefer to set up the build yourself:
Create a new project folder:
mkdir my-preact-app && cd my-preact-app
Initialize package.json
:
npm init -y
Install Preact:
npm install preact
For convenient JSX handling, install the following dependencies:
npm install --save-dev babel-core babel-loader @babel/preset-env @babel/preset-react webpack webpack-cli html-webpack-plugin webpack-dev-server
Note: For Preact, we recommend replacing @babel/preset-react
with a specialized preset or configuring an alias in webpack to ensure JSX compiles correctly for Preact.
In the webpack.config.js
file, add the following:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
['@babel/preset-react', {
pragma: 'h',
pragmaFrag: 'Fragment'
}]
]
}
}
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
filename: 'index.html',
}),
],
devServer: {
static: './dist',
compress: true,
port: 8080
}
};
In Vite, you don’t have to manually specify an alias for Preact. However, if you are migrating old React code, you can use preact/compat
:
import { useState } from 'preact/compat';
Create a simple src/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My Preact App</title>
</head>
<body>
<div id="root"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
Create a src/index.js
file that will handle our Preact code:
import { h, render } from 'preact';
function App() {
return <h1>Hello, Preact!</h1>;
}
render(<App />, document.getElementById('root'));
Run the build to make sure everything works as it should:
npx webpack serve --mode development
Open http://localhost:8080
in your browser (if using a local machine) or http://<SERVER_IP>:8080
if using a server. You should see “Hello, Preact!”.
Now, let's move on to how to create and deploy a Preact application on the Hostman App Platform. The service simplifies the deployment process with ready-to-use "apps" that you can launch in just a few minutes. You don't need to waste time manually setting up the environment or updating servers — everything works "out of the box."
Create a Git repository and upload it to GitHub. Use the following commands:
git init
git add .
git commit -m "Initial commit"
Go to the GitHub web interface, log in to your account, and click the green New button to create a new repository.
Set the repository name and type — public or private. When linking your GitHub account in Hostman, you can use either type. If you're adding a repository by URL, it must be public.
After creating the repository, use this command:
git remote add origin <repository URL>
Push your changes to the remote repository:git push -u origin main
Go to the Hostman dashboard, select the "Apps" section, and click "Add." In the "Type" tab, stay on the "Frontend" tab and select the Preact application. Choose the Node.js version for your project (usually Node.js 22).
Link your GitHub account or connect the Git repository via URL.
Select your repository from the list.
Choose the configuration. For frontend projects, Hostman provides a configuration with 200,000 requests and 1 GB of NVMe. This will be enough for testing your applications.
Specify the build command and the build directory. By default, the build command is npm run build — regardless of how the project was created.
Run the application deployment and wait for it to finish.
Hostman will automatically link a free technical domain to your app with an SSL certificate from Let's Encrypt. The application will immediately be available on this domain. To link your custom domain, refer to our guide.
With the auto-deployment enabled, the App Platform will monitor changes in the repository and automatically update the application when new commits are made.
If a deployment error occurs, check the compatibility of the Node.js environment version — on the app's page, go to the "Settings" tab, and in the "Deployment Settings" section, click the "Edit" button in the "Environment Version" field.
In a small application, one page might be enough. However, if you need to switch between screens and manage global state (such as user accounts), you can add the following libraries:
preact-router
— for routing between pages.
preact/hooks
— for hooks (useState
, useEffect
). These are already built into Preact, you just need to import them from preact/hooks
.
Use this code in components/app.js
to implement routing:
import { h, render } from 'preact';
import Router from 'preact-router';
import Home from './Home';
import About from './About';
function App() {
return (
<div>
<Router>
<Home path="/" />
<About path="/about" />
</Router>
</div>
);
}
In addition to the standard hooks, the Preact ecosystem includes an interesting tool — Preact Signals. It provides signals that automatically notify the components where they are used when they change, without the need for complex state setter management.
Here’s an example of using Signals in components/app.js
:
import { signal } from '@preact/signals-core';
import { h, render } from 'preact';
const count = signal(0);
function App() {
return (
<div>
<p>Value: {count.value}</p>
<button onClick={() => count.value++}>Increase</button>
</div>
);
}
The variable count
becomes "live": every time count.value
changes, the component is automatically re-rendered.
Signals simplify the logic of working with values, eliminating the need to manually call setState
or use other hooks.
Signals can be a convenient alternative to Redux, Context API, and even traditional React hooks if your project requires frequent UI updates or a simple way to synchronize data.
As your application grows, one of the key performance factors becomes the speed of the initial load. Instead of delivering the entire code to the user at once, you can split the bundle into smaller parts and load them as needed. This is especially important in modern SPA (Single Page Applications), where some pages or functional modules may not be required right away.
Reducing the initial load size: The user only downloads the files needed for the initial screen. Other parts of the code are loaded when the user navigates to specific pages or activates certain functionality.
Faster rendering: With a smaller bundle size, the application starts rendering faster, positively affecting UX and potentially improving Lighthouse metrics.
Optimizing traffic: If the user does not navigate to certain sections, the code for those sections may not be loaded at all, saving bandwidth and reducing server load.
You can split the code into separate chunks using JavaScript's built-in feature — dynamic import. When using Vite, dynamic import automatically creates separate bundles thanks to built-in support for ES modules.
import { h } from 'preact';
import { Suspense, lazy } from 'preact/compat';
// Lazy loading the component (the code for this component will be in a separate bundle).
const About = lazy(() => import('./About'));
function App() {
return (
<div>
<h1>Home Page</h1>
<Suspense fallback={<div>Loading...</div>}>
<About />
</Suspense>
</div>
);
}
export default App;
In this example:
lazy(() => import('./About'))
tells the bundler to put the About component code in a separate bundle.
Suspense
is used to display a fallback component while the About component is loading.
If the user doesn't navigate to the section where the About
component is used, the necessary code will not be loaded, reducing the size of the initial bundle.
Settings Features:
In Vite, Code Splitting works automatically, and dynamic imports create separate files without additional configuration.
The fallback component in Suspense is optional, but it improves UX by showing a loading indicator.
Thanks to Code Splitting, users will see your application's interface more quickly. Instead of one large file, the app is split into several parts, and each part loads as needed. The logic of Preact (JSX, hooks, state) remains intact, and transitions between pages and modules become smoother as only the necessary code is kept in memory.
Group similar pages: If you have several sections with similar purposes (e.g., documentation or analytics dashboards), it makes sense to load them in one go to avoid creating too many small requests to the server.
Watch critical paths: Keep only the necessary elements for rendering the first page (Hero, menu, header) in the main bundle. Move everything else to dynamic imports.
Use profiling: Preact DevTools or browser development tools can help identify which chunks of code are loaded most frequently and optimize them for faster delivery.
Symptom |
Problem |
Solution |
Component doesn't render or renders partially |
A functional component uses React-specific imports (e.g., import React from 'react' or ReactDOM is incorrectly connected), but in Preact, you should use |
Check and replace all React-related imports with the corresponding Preact ones. Make sure that the bundler configuration (e.g., webpack) is set to alias React to |
"createContext is not a function" error |
React's context is being used, but the Preact version (especially an older one) may not support all context features (or they are named differently). |
Make sure you are using the latest version of Preact. If needed, connect Preact Compat ( |
Issues with hooks (useState, useEffect, etc.) |
Code is written with React hooks in mind, which may behave differently or be absent in older versions of Preact. If migration is incomplete, "hardcoded" React imports might remain. |
Make sure the latest version of Preact with hooks support is used. Import all hooks directly from Preact: |
Routing doesn't work (error when switching pages) |
React Router is used without considering Preact's specifics (e.g., |
Replace react-router-dom with preact-router or similar alternatives. Check that all route components are correctly connected. Set up the right paths and ensure no conflicts with the router in the build configuration. |
Content doesn't work correctly in some components |
React may have used specific props (e.g., children or ref), which in Preact require different approaches or are available under different names. |
Check the differences in the API (especially with ref and children). Use compatible syntax (via Preact Compat) or adapt component code based on Preact’s features. |
React DevTools plugin doesn't see components |
React DevTools is not directly compatible with a Preact app (especially if |
Install and configure Preact DevTools or use React DevTools with |
Testing issues (Jest, Testing Library) |
Testing environments are set up for React ( |
Use |
Preact is an excellent choice for lightweight and fast client-side applications. Its minimalist core simplifies development, and its high compatibility with the React ecosystem makes migration easy. Paired with Hostman, you get the convenience of deployment: you can set up an app in minutes, update code through Git, and avoid unexpected server crashes.