A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

at trunk 91 lines 5.4 kB View raw View rendered
1# 0001 — Framework & application stack 2 3- **Status:** Accepted 4- **Date:** 2026-06-08 5- **Scope:** Project-wide (SP0 and all later sub-projects inherit this) 6 7## Context 8 9SkyPress has two workloads with opposite characteristics living in one app: 10 111. **The editor** — a heavy, fundamentally *client-side* React application. It embeds 12 Automattic's `@automattic/isolated-block-editor`, which bundles its own copy of the 13 `@wordpress/*` packages (block-editor, block-library, components, …). This cannot be 14 server-rendered; it boots in the browser. 152. **Public reading pages** — must be *fast and light* (brief §6, §10): edge/SSR-rendered 16 HTML, minimal client JS, never shipping Gutenberg's weight to readers. 17 18A single decision-gating fact constrains the whole stack. Verified directly from the npm 19registry on 2026-06-08: 20 21- `@automattic/isolated-block-editor@2.30.0` (latest; republished 2026-01-29 after being 22 dormant since 2024-03 — so it **is** maintained). 23- It declares **no `peerDependencies`** and pulls React transitively through 24 `@wordpress/element@6.24.0`, which depends on **`react@^18.3` / `react-dom@^18.3`**. 25- Therefore the editor requires **React 18**, and the host app must share a **single** 26 React 18 instance with it (React forbids two copies — hooks break otherwise). 27 28This makes React 19 (the default in Next.js 15 App Router) the wrong default path. 29 30## Options considered 31 32| Option | React story | Public-page story | Free host | Verdict | 33|---|---|---|---|---| 34| **Next.js 15 (App Router)** | Defaults to React 19; running RSC with pinned React 18 is fighting the framework | Good (RSC/edge) | Vercel | ✗ React-19 default fights the editor's React-18 lock; RSC adds friction for a fundamentally client-side editor | 35| **Next.js 14 (Pages/App)** | React 18 | Good | Vercel | △ Viable but a step back onto an older line; heavier than needed for mostly-static reading pages | 36| **Remix / React Router v7** | React 18-friendly | Good (loaders/SSR) | Vercel/CF | △ Solid, but every page carries the React runtime; less ideal for ~0-JS reading pages | 37| **Vite + React 18 SPA only** | Full control of React version | Poor — no native SSR for light reading pages | static + functions | △ Great for the editor, but I'd have to bolt on SSR for §6 | 38| **Astro + React 18 islands** | Pin `react@18.3` explicitly; no framework default to fight | Excellent — Astro's core strength is ~0-JS HTML pages | Vercel **or** Cloudflare adapter | ✅ **Chosen** | 39 40## Decision 41 42**Astro 6 + React 18 (`react@18.3.1`), React used only via islands.** One Astro project: 43 44- `/editor` (and similar authoring routes) host the editor as a single 45 `client:only="react"` island. Gutenberg's bundle loads **only** on that route and is 46 never server-rendered — exactly matching how the editor must boot. 47- Public routes (`/`, `/<handle>`, `/<handle>/<slug>`) are Astro pages rendered to light 48 HTML at build/edge time. They render the stored block tree to HTML with the **same** 49 `@wordpress/blocks` + `@wordpress/block-library` versions the editor bundles 50 (`14.13.0` / `9.24.0`), so reading pages ship no editor JS. 51- Server endpoints (OAuth callbacks, blob proxy, etc.) are Astro endpoints — added per 52 sub-project as needed. 53- React is pinned to `18.3.1` at the workspace root and **deduped** so the app and the 54 editor share one instance. The SP0 spike verifies this empirically (no "invalid hook 55 call" / duplicate-React errors). 56 57Hosting target (Vercel vs Cloudflare) is deferred to SP7; Astro keeps both open via 58`@astrojs/vercel` / `@astrojs/cloudflare`. Leaning Cloudflare for the read-through 59renderer, with the OAuth-client runtime decision (SP1) able to influence it. 60 61## Why 62 63- **React 18 is non-negotiable** for the editor; Astro lets me pin it cleanly instead of 64 fighting a framework's React-19 default. 65- **Bundle isolation comes for free**: Astro ships per-island JS, so the heavy editor 66 bundle is physically confined to authoring routes and reading pages stay light — 67 directly satisfying brief §6/§10 ("keep reading pages fast and light despite 68 Gutenberg's heft") without manual code-splitting gymnastics. 69- **One app, dual deploy**: Astro adapters keep both free-tier hosts on the table (§8). 70- **Vite underneath** gives modern, fast bundling and good control over the awkward 71 `@wordpress/*` packages. 72 73## Consequences & risks 74 75- **Gutenberg-under-Vite bundling is the open risk** (the `@wordpress/*` packages 76 sometimes assume a global `wp` and ship CSS). This is precisely what the SP0 spike 77 de-risks empirically. **Fallback if Astro/Vite can't bundle the editor cleanly:** a 78 two-surface monorepo — a plain Vite React SPA for the editor + Astro for public pages — 79 or Next.js 14. Documented here so the fallback is pre-decided. 80- **Version coupling** to `isolated-block-editor`'s pinned `@wordpress/*` line is a 81 maintenance risk to track (brief §4). Render-path packages are pinned to the same 82 versions to avoid drift. 83- Astro's React integration must tolerate a very large `client:only` island; verified in 84 SP0. 85 86## Verification 87 88Empirically proven by the SP0 spike: editor boots, `onSaveBlocks` yields a block array, 89that array round-trips to HTML via the shared `@wordpress/blocks`, and the production 90build reports the editor JS confined to the authoring route. See 91`docs/specs/sp0-foundations-editor-spike.md`.