Astro Tutorial – Working with Preact

Inhaltsverzeichnis

TL;DR

Code for this tutorial is on Github

Starter Project

Create Starter

❯ npm create astro@latest "Tutorial - Working with Preact"
.../21.6.2/pnpm/store/v3/tmp/dlx-99714   |  +39 ++++
.../21.6.2/pnpm/store/v3/tmp/dlx-99714   | Progress: resolved 39, reused 39, downloaded 0, added 39, done

 astro   Launch sequence initiated.

      ◼  dir Using Tutorial - Working with Preact as project directory

  tmpl   How would you like to start your new project?
         Empty

    ts   Do you plan to write TypeScript?
         Yes

   use   How strict should TypeScript be?
         Strict

  deps   Install dependencies?
         Yes

   git   Initialize a new git repository?
         Yes

      ✔  Project initialized!
         ■ Template copied
         ■ TypeScript customized
         ■ Dependencies installed
         ■ Git initialized

  next   Liftoff confirmed. Explore your project!

         Enter your project directory using cd "./Tutorial - Working with Preact"
         Run pnpm dev to start the dev server. CTRL+C to stop.
         Add frameworks like react or tailwind using astro add.

         Stuck? Join us at https://astro.build/chat

╭─────╮  Houston:
│ ◠ ◡ ◠  Good luck out there, astronaut! 🚀
╰─────╯

Run Starter

❯ cd "Tutorial - Working with Preact"
❯ npm run dev

> tutorial---working-with-preact@0.0.1 dev /Users/Shared/CLOUD/Programmier-Workshops/Kurse/Astro/Einsteiger/Tutorial - Working with Preact
> astro dev


 astro  v4.5.13 ready in 156 ms

┃ Local    http://localhost:4321/
┃ Network  use --host to expose

14:34:39 watching for file changes...

Modify Project Structur and add helper code

tsconfig.json

{
  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "allowJs": true,
    "strictNullChecks": true,
    "baseUrl": ".",
    "lib": ["es2022", "dom", "dom.iterable"],
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "exclude": ["node_modules", "**/node_modules/*", ".vscode", "dist"]
}

Replace relative path with reference to path alias in every page

import Layout from '../../layouts/BlogPost.astro';

becomes to

import Layout from '@/layouts/BlogPost.astro';
import Layout from '../styles/global.css';

becomes to

import Layout from '@/styles/global.css';

Install Preact Integration

Add integration for preact

❯ npx astro add preact
✔ Resolving packages...

  Astro will run the following command:
  If you skip this step, you can always run it yourself later

 ╭──────────────────────────────────────────────────╮
 │ pnpm add @astrojs/preact@^3.1.2 preact@^10.20.1  │
 ╰──────────────────────────────────────────────────╯

✔ Continue? … yes
✔ Installing dependencies...

  Astro will make the following changes to your config file:

 ╭ astro.config.mjs ─────────────────────────────╮
 │ import { defineConfig } from 'astro/config';  │
 │                                               │
 │ import preact from "@astrojs/preact";         │
 │                                               │
 │ // https://astro.build/config                 │
 │ export default defineConfig({                 │
 │   integrations: [preact()]                    │
 │ });                                           │
 ╰───────────────────────────────────────────────╯

✔ Continue? … yes

   success  Added the following integration to your project:
  - @astrojs/preact

  Astro will make the following changes to your tsconfig.json:

 ╭ tsconfig.json ───────────────────────────────────────────────────────╮
 │ {                                                                    │
 │   "extends": "astro/tsconfigs/strict",                               │
 │   "compilerOptions": {                                               │
 │     "allowJs": true,                                                 │
 │     "strictNullChecks": true,                                        │
 │     "baseUrl": ".",                                                  │
 │     "lib": [                                                         │
 │       "es2022",                                                      │
 │       "dom",                                                         │
 │       "dom.iterable"                                                 │
 │     ],                                                               │
 │     "paths": {                                                       │
 │       "@/*": [                                                       │
 │         "src/*"                                                      │
 │       ]                                                              │
 │     },                                                               │
 │     "jsx": "react-jsx",                                              │
 │     "jsxImportSource": "preact"                                      │
 │   },                                                                 │
 │   "exclude": [                                                       │
 │     "node_modules",                                                  │
 │     "**/node_modules/*",                                             │
 │     ".vscode",                                                       │
 │     "dist"                                                           │
 │   ]                                                                  │
 │ }                                                                    │
 ╰──────────────────────────────────────────────────────────────────────╯

✔ Continue? … yes

   success  Successfully updated TypeScript settings

Install preact/signale

npm install preact/signal

Modify src/pages/index.astro

---
import Counter from "@/components/Counter";
import { signal } from "@preact/signals";

const count = signal(0);
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <meta name="generator" content={Astro.generator} />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <style>
      html,
      body {
        font-family: system-ui;
        margin: 0;
      }
      body { padding: 2rem; }
      h1 { text-align: center; }
    </style>
  </head>
  <body>
    <main>
      <h1 style="halign:center">Working with Astro and Preact</h1>
      <Counter count={count} client:visible><h3>Hello, Preact 1!</h3></Counter>
      <Counter count={count} client:visible><h3>Hello, Preact 2!</h3></Counter>
    </main>
  </body>
</html>

Create Component Counter

Create folder src/components

mkdir src/components

Create file Counter.tsx

import type { ComponentChildren } from "preact";
import type { Signal } from "@preact/signals";
import { lazy, Suspense } from "preact/compat";

import "./Counter.css";

const Message = lazy(async () => import("./Message"));
const Fallback = () => <p>Loading...</p>;

type Props = {
  children: ComponentChildren;
  count: Signal<number>;
};

export default function Counter({ children, count }: Props) {
  const add = () => count.value++;
  const subtract = () => count.value--;

  return (
    <>
      <div class="counter">
        <button onClick={subtract}>-</button>
        <pre>{count}</pre>
        <button onClick={add}>+</button>
      </div>
      <Suspense fallback={Fallback}>
        <Message>{children}</Message>
      </Suspense>
    </>
  );
}

Create file Counter.css

.counter {
	display: grid;
	font-size: 2em;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	margin-top: 2em;
	place-items: center;
}

Create Message.tsx

import type { ComponentChildren } from 'preact';
import './Message.css';

export default function Message({ children }: { children: ComponentChildren }) {
	return <div class="message">{children}</div>;
}

Create Message.css

.message {
	text-align: center;
}

Run App

npm run dev