From 330167739643c376803614c23dee973c77b2a70f Mon Sep 17 00:00:00 2001 From: oscarvz Date: Mon, 25 Nov 2024 17:24:57 +0100 Subject: [PATCH 01/18] First steps article --- .../blog/2024-11-25-client-side-logic.mdx | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 www/src/content/blog/2024-11-25-client-side-logic.mdx diff --git a/www/src/content/blog/2024-11-25-client-side-logic.mdx b/www/src/content/blog/2024-11-25-client-side-logic.mdx new file mode 100644 index 000000000..150be4816 --- /dev/null +++ b/www/src/content/blog/2024-11-25-client-side-logic.mdx @@ -0,0 +1,117 @@ +--- +title: "Adding client-side logic to your Hono app" +description: A guide to adding client-side logic to your Hono app. +slug: client-side-logic +date: 2024-11-25 +author: Oscar van Zijverden +tags: + - guide + - tutorial + - honc + - hono + - javascript + - api +--- + +import { Aside, FileTree, TabItem, Tabs } from "@astrojs/starlight/components"; + +You just built a great app that gets rendered server-side with Hono. +But now you want to add some client-side logic to it - and Hono ships their own +React-like API to do just that! + +In this guide we'll be going over on how to add client-side logic to a Hono app +and unlock the full potential of your project. + +## Before getting started + +We're building an app that uses Hono running in Cloudflare Workers, leveraging +[static asset bindings](https://developers.cloudflare.com/workers/static-assets/binding/) +to serve the app, though the same principles apply to any other way of serving +your app. + +As Hono is a server-side app by default, we'll need to make a distinction +between server-side and client-side logic and adjust our build step to +facilitate this.\ +We'll be using [Vite](https://vitejs.dev) with a few plugins to make this +happen. + +## Let's build! + +First, let's get started with a new Hono app by running the following command: + + + +```sh +npm create hono@latest hono-client +``` + + + +```sh +yarn create hono hono-client +``` + + + +```sh +pnpm create hono hono-client +``` + + + +```sh +bunx create-hono hono-client +``` + + + + + +The `src` directory will contain a single `index.ts` file with a simple Hono +app. We'll be adding three other files to the `src` directory: + + + +- src + - index.ts + - **api.ts** api routes + - **web.ts** client routes + - client + - **index.tsx** this file will contain the logic to mount the app on the client + + + +To run and build the app, we're going to install Vite and two Hono plugins to +facilitate client-side logic: + + + +```sh +npm install vite +npm install -D @hono/vite-build @hono/vite-dev-server +``` + + + +```sh +yarn add vite +yarn add -D @hono/vite-build @hono/vite-dev-server +``` + + + +```sh +pnpm add vite +pnpm add -D @hono/vite-build @hono/vite-dev-server +``` + + + +```sh +bun add vite +bun add -D @hono/vite-build @hono/vite-dev-server +``` + + From 8c82b70c50d1f3449d8eaf31a50b37dab5eb64aa Mon Sep 17 00:00:00 2001 From: oscarvz Date: Mon, 25 Nov 2024 21:24:03 +0100 Subject: [PATCH 02/18] Additional instructions --- .../blog/2024-11-25-client-side-logic.mdx | 75 +++++++++++++++++-- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/www/src/content/blog/2024-11-25-client-side-logic.mdx b/www/src/content/blog/2024-11-25-client-side-logic.mdx index 150be4816..83f078963 100644 --- a/www/src/content/blog/2024-11-25-client-side-logic.mdx +++ b/www/src/content/blog/2024-11-25-client-side-logic.mdx @@ -10,7 +10,6 @@ tags: - honc - hono - javascript - - api --- import { Aside, FileTree, TabItem, Tabs } from "@astrojs/starlight/components"; @@ -35,9 +34,9 @@ facilitate this.\ We'll be using [Vite](https://vitejs.dev) with a few plugins to make this happen. -## Let's build! +## Let us build! -First, let's get started with a new Hono app by running the following command: +First, let's get started with scaffolding a new Hono app: @@ -70,19 +69,79 @@ Make sure to select the `cloudflare-workers` template when prompted. The `src` directory will contain a single `index.ts` file with a simple Hono -app. We'll be adding three other files to the `src` directory: +app. We'll be adding a `client` directory with an index and component: - src - index.ts - - **api.ts** api routes - - **web.ts** client routes - - client - - **index.tsx** this file will contain the logic to mount the app on the client + - **client** + - **index.tsx** logic to mount the app on the client + - **Counter.tsx** component to demonstrate client-side logic +We set up a simple counter component that increments a count when a button is +clicked: + +```tsx title="src/client/Counter.tsx" +import { useState } from "hono/jsx"; + +export function Counter() { + const [count, setCount] = useState(0); + + return ( +
+ + Count: {count} +
+ ); +} +``` + +// add client/index.tsx setup + +We're going to add some JSX to the `src/index.ts` file, so we first need to +change the file extension to `.tsx`. +Once that's done, we can add Hono's +[JSX renderer middleware](https://hono.dev/docs/middleware/builtin/jsx-renderer) +to the `/` route and return the `` component: + +```tsx title="src/index.tsx" {2, 4, 8-23, 26} +import { Hono } from "hono"; +import { jsxRenderer } from "hono/jsx-renderer"; + +import { Counter } from "./client/Counter"; + +const app = new Hono(); + +app.use( + "/", + jsxRenderer( + ({ children }) => ( + + + + + hono + + {children} + + ), + { docType: true }, + ), +); + +app.get("/", (c) => { + return c.render(); +}); + +export default app; +``` + + To run and build the app, we're going to install Vite and two Hono plugins to facilitate client-side logic: From 2645cd10ed4c54dabd0285970bacb6571e1a8d16 Mon Sep 17 00:00:00 2001 From: oscarvz Date: Tue, 26 Nov 2024 14:21:42 +0100 Subject: [PATCH 03/18] Update examples & add build steps --- .../blog/2024-11-25-client-side-logic.mdx | 204 ++++++++++++++++-- 1 file changed, 191 insertions(+), 13 deletions(-) diff --git a/www/src/content/blog/2024-11-25-client-side-logic.mdx b/www/src/content/blog/2024-11-25-client-side-logic.mdx index 83f078963..c796b950f 100644 --- a/www/src/content/blog/2024-11-25-client-side-logic.mdx +++ b/www/src/content/blog/2024-11-25-client-side-logic.mdx @@ -1,8 +1,8 @@ --- -title: "Adding client-side logic to your Hono app" +title: "Guide: Adding client-side logic to your Hono app" description: A guide to adding client-side logic to your Hono app. slug: client-side-logic -date: 2024-11-25 +date: 2024-11-26 author: Oscar van Zijverden tags: - guide @@ -25,8 +25,7 @@ and unlock the full potential of your project. We're building an app that uses Hono running in Cloudflare Workers, leveraging [static asset bindings](https://developers.cloudflare.com/workers/static-assets/binding/) -to serve the app, though the same principles apply to any other way of serving -your app. +\- though the same principles apply to other supported environments too. As Hono is a server-side app by default, we'll need to make a distinction between server-side and client-side logic and adjust our build step to @@ -38,7 +37,7 @@ happen. First, let's get started with scaffolding a new Hono app: - + ```sh npm create hono@latest hono-client @@ -81,6 +80,8 @@ app. We'll be adding a `client` directory with an index and component: +### Adding component & mounting point + We set up a simple counter component that increments a count when a button is clicked: @@ -101,7 +102,50 @@ export function Counter() { } ``` -// add client/index.tsx setup +Then we import the component & hydrate it in the client entry file: + +```tsx title="src/client/index.tsx" +import { StrictMode } from "hono/jsx"; +import { hydrateRoot } from "hono/jsx/dom/client"; + +import { Counter } from "./Counter"; + +const root = document.getElementById("root"); +if (!root) { + throw new Error("Root element not found"); +} + +hydrateRoot( + root, + + + , +); +``` + + + +You code editor might give you a hint that `document` is not defined. Given we +added the client-side logic, we need to tell TypeScript that we're running in a +browser environment: + +```json title="tsconfig.json" ins={6} +{ + "compilerOptions": { + //... + "lib": [ + "ESNext", + "DOM", + ], + // ... + }, +} +``` We're going to add some JSX to the `src/index.ts` file, so we first need to change the file extension to `.tsx`. @@ -109,7 +153,7 @@ Once that's done, we can add Hono's [JSX renderer middleware](https://hono.dev/docs/middleware/builtin/jsx-renderer) to the `/` route and return the `` component: -```tsx title="src/index.tsx" {2, 4, 8-23, 26} +```tsx title="src/index.tsx" ins={2, 4, 8-24, 27} del={26} import { Hono } from "hono"; import { jsxRenderer } from "hono/jsx-renderer"; @@ -118,16 +162,15 @@ import { Counter } from "./client/Counter"; const app = new Hono(); app.use( - "/", jsxRenderer( ({ children }) => ( - hono + hono-client - {children} +
{children}
), { docType: true }, @@ -135,17 +178,23 @@ app.use( ); app.get("/", (c) => { + return c.text("Hello Hono!"); return c.render(); }); export default app; ``` +We're almost there. If you run the app now with the `dev` script, you'll get an +error. Let's fix that! -To run and build the app, we're going to install Vite and two Hono plugins to -facilitate client-side logic: +### Adding build step and scripts - +As we just added client-side logic. This needs to be built separately from the +Wrangler build step. We'll be using Vite and two plugins to facilitate both +build steps: + + ```sh npm install vite @@ -174,3 +223,132 @@ bun add -D @hono/vite-build @hono/vite-dev-server ``` + +In the root of your project, create a `vite.config.ts` file. We'll define the +config for both the client-side build and the server-side build. + +Note that for the client build the `outDir` is set to `./public`. As mentioned +earlier we're using Cloudflare Workers with static asset bindings. If we're not +defining these bindings ourselves, the worker will automagically find the files +in the `public` directory. + +```ts title="vite.config.ts" +import build from "@hono/vite-build/cloudflare-workers"; +import devServer from "@hono/vite-dev-server"; +import cloudflareAdapter from "@hono/vite-dev-server/cloudflare"; +import { defineConfig } from "vite"; + +export default defineConfig(({ mode }) => { + if (mode === "client") { + return { + build: { + rollupOptions: { + input: "./src/client/index.tsx", + }, + outDir: "./public", + }, + }; + } + + const entry = "./src/index.tsx"; + return { + server: { port: 8787 }, + plugins: [ + devServer({ adapter: cloudflareAdapter, entry }), + build({ entry }), + ], + }; +}); +``` + +Now we need to adjust the `package.json` scripts to facilitate the new build +steps. Additionally, we set the type to `module` to allow for ESM imports: + +```json title="package.json" del={5} ins={6, 7} +{ + // ... + "type": "module", + "scripts": { + "dev": "wrangler dev", + "dev": "vite dev", + "build": "vite build --mode client && vite build", + } + // ... +} +``` + +### Access the built script + +As a final step we need to load the client-side script. We'll add a script tag +to the JSX renderer.\ +We need to make a distinction between a development and +production environment. Vite allows us to do this easily with it's +[built-in env](https://vite.dev/guide/env-and-mode#env-variables); +for the dev environment we can load the client's `.tsx` file; for production we +can get it from the `public` directory. + +First we add the `vite/client` types to the TypeScript config: + +```json file="tsconfig.json" ins={6} +{ + "compilerOptions": { + //... + "types": [ + // ... + "vite/client", + ], + //... + }, +} +``` + +Then we adjust the `src/index.tsx` file to load the client-side script, +depending on the environment: + +```tsx title="src/index.tsx" ins={12-17} +// ... +app.use( + jsxRenderer( + ({ children }) => ( + + + + + hono-client + + {import.meta.env.PROD ? ( +