DEV Community

Sam Markham
Sam Markham

Posted on • Updated on

Getting started in Preact (by rebuilding a React app)

A few weeks ago, after reading through the Preact docs, I decided to make a version of my React writing prompt generator in Preact to get more familiar with the library.

For those who haven’t heard of it (like I hadn’t!), Preact is a small, lightweight library with a lot of similar functionality to React, other than a few key differences. Preact more closely follows DOM specifications (e.g. it allows the use of class instead of className, though both are supported), and it uses the browser’s native event system instead of a synthetic one like React does, to name a couple.

The Preact documentation states that it’s possible to move React apps over to Preact via aliasing react and react-dom to preact/compat (short for compatibility), and it has some information on how this is configured in various bundlers, but because I wanted to get more familiar with using Preact directly, I decided to start with Preact from the get-go and just move some of my code over manually instead.

This is what I did!

1. Start with the Preact CLI

Following along with the docs, I started by installing the Preact CLI on my computer, and then using it to create a new project:

npm install -g preact-cli
Enter fullscreen mode Exit fullscreen mode
preact create default preact-writing-prompt-generator
Enter fullscreen mode Exit fullscreen mode

The default flag here refers to the default template for the Preact CLI, available on GitHub here. I ended up having some minor issues with this set up later, but it was a good place to start because it gave me some code right out of the box that I could tinker with instead of building from scratch.

Following along with the tutorial, I spun up a development server, and then started poking around the files.

cd preact-writing-prompt-generator

npm run dev

Enter fullscreen mode Exit fullscreen mode

From here, it was time to rebuild my components.

2. JSX in Preact, and alternatives

There are differences between React and Preact components, but not as many as I thought there would be going into this endeavour. Part of this is, of course, because while it doesn’t include every React feature (by design!), Preact was designed to achieve 100% compatibility with React with the preact/compat layer.

Without using preact/compat though, I still had a few options for how to set up my Preact components.

By default, React works with JSX, a syntactic extension to JavaScript that allows you to code what basically looks like HTML directly in JavaScript. Using JSX requires using a build step: the JSX code needs to be converted into JavaScript functions, which then make up the virtual DOM (used to update the real DOM).

Preact can be set up without using JSX, beneficial if you’d like to avoid a build step.

This is an example of setting up Preact to work directly in the browser, without JSX, from the Preact docs:

<script type="module">
  import { h, Component, render } from '';

  // Create your app
  const app = h('h1', null, 'Hello World!');

  render(app, document.body);
Enter fullscreen mode Exit fullscreen mode

Preact components can also be built with Hyperscript/HTM, which is a syntax that looks similar to JSX but doesn’t require a build step (see another example from the Preact docs):

<script type="module">
  import { h, Component, render } from '';
  import htm from '';

  // Initialize htm with Preact
  const html = htm.bind(h);

  function App (props) {
    return html`<h1>Hello ${}!</h1>`;

  render(html`<${App} name="World" />`, document.body);
Enter fullscreen mode Exit fullscreen mode

However, as I was already comfortable working in JSX from my experience with React, and as I wasn’t bothered by having a build step in this project, I decided to go with that.

Without using preact/compat though, JSX still looks a little different in Preact.

Basically, to use some more example code from the Preact docs, JSX that looks like this:

<a href="/">
Enter fullscreen mode Exit fullscreen mode

would look like this in React when converted to function calls to build the virtual DOM:

  { href:'/' },
  React.createElement('span', null, 'Home')
Enter fullscreen mode Exit fullscreen mode

and like this in Preact (without using preact/compat):

  { href:'/' },
  h('span', null, 'Home')
Enter fullscreen mode Exit fullscreen mode

As far as I can tell, Preact uses a custom pragma (a “directive” that tells a compiler how to handle the input), named h by the creator of Preact because the original idea for a JSX-like builder function was called hyperscript, to do pretty much the same thing React’s React.createElement() is doing. Because this function isn’t namespaced to Preact though, it has to be imported separately.

What both of these ways of writing JSX have in common, is that they tell the transpiler (in this case it's Babel, which both React and this implementation of Preact use) that this h function should be called at runtime for each node.

3. Components

With the way I was going to write my components figured out, I could start moving them over from my React project and editing them into Preact syntax!

import { h } from 'preact';
import { Router } from 'preact-router';

import Header from './header';
import Home from '../routes/home';
import Nautical from '../routes/nautical'
import About from '../routes/about'

const App = () => (
  <div id="app">
    <Header />
      <Home path="/" />
      <Nautical path="/nautical"/>
      <About path="/about"/>

export default App;
Enter fullscreen mode Exit fullscreen mode

This component imports h from Preact instead of React from React, but there aren’t any differences as to how that affects this component.

It also uses preact-router instead of react-router-dom, which does feature some different syntax. For example, in React, the routes in the code snippet above would be written like this:

// note that in React, Router is called Browser Router, but it is convention to import BrowserRouter as Router
  <Route path=/><Home/></Route>
  <Route path=/><Nautical/></Route>
  <Route path=/><About/></Route>
Enter fullscreen mode Exit fullscreen mode

Other than that, Preact components can pretty much look like React components!


Overall I really enjoyed the experience of translating the app I’d built into the Preact library! The main difficulty I ran into was figuring out how JSX worked in Preact, and that was mostly just to write this post—the Preact templates were intuitive to use even without that information. (Though on that note, thank you to Nick Taylor for sending me this article, which helped a lot!)

The other stumbling block for me was that the file structure in the Preact CLI was quite different from what I’m used to setting up for React projects. As far as I can tell that was specific to that template though, not related to the library itself, and I was still able to figure it out quite quickly even if it isn’t how I would usually set up my own project.

I hope this post is useful to anyone else thinking of trying out Preact, and if you have questions, comments, or suggested corrections, please let me know in the comments!

Top comments (2)

nickytonline profile image
Nick Taylor • Edited

Nice write up Sam! Yeah, with preact/compat you could still use React router, but if the preact router suited your needs, all good.

For the className prop, you can use either class or className in Preact. If you wanted to standardize it in your code, you could probably find an eslint rule for that.

There's a lot of talk about moving away from bundlers, so something, so htm is interesting.

Although, not Preact specific, Luke Jackson, from Formidable labs has been doing some interesting work. Their post, Don’t Build That App!, is a great read and uses the htm package like you demonstrated.

Looking forward to your next post!

sharkham profile image
Sam Markham

Thanks so much Nick!

And thanks for the specific notes and the link to the article too--it was a great read, and I really appreciated both the step-by-step walkthrough and the added background on htm.

(I'm off to fix the class/className thing now, I knew I was missing something!)