swyxio/swyxdotio

swyxio/swyxdotio is a UI-focused project on GitHub with 413 stars, written primarily in JavaScript. This is the repo for swyx's blog - Blog content is created in github issues, then posted on swyx.io as blog pages! Comment/watch to follow along my blog within GitHub
Snapshot summary built from the project's own GitHub metadata — there's no written TopGit review yet. The page will update automatically when a full review is published.
TopGit writes full reviews for the most-starred, most-requested repositories. This page is a snapshot until then — see the READ ME tab for the original README in full.
Snapshot
Top contributors
Show top contributors
swyx's personal site
swyx's personal site, using:
- SvelteKit 2 + Svelte 5
- Tailwind 3 + Tailwind Typography
marked+shikifor markdown rendering (replaced mdsvex/remark)- Cloudflare Workers with Static Assets (hybrid: prerendered static pages + on-demand SSR posts, edge-cached)
- GitHub Issues as CMS
If you want to make a site based on this, see https://github.com/swyxio/swyxkit for a cleaner starter template
Architecture / rendering
- Static (prerendered at build):
/about,/portfolio,/subscribe. - Dynamic + edge-cached:
/,/ideas,/podcasts,/[slug],/rss.xml,/sitemap.xml,/og/*, and selected/api/*routes. Rendered on demand on Cloudflare Workers and stored in the Cache API viasrc/hooks.server.jsuntils-maxageexpires, so new posts appear without a rebuild and serving is O(1). Versioned OG responses and public read-count GETs retain their public cache headers; most other cached dynamic responses are returned to the browser asprivate, no-storeto avoid an additional cache layer outside the Worker. - Durable content manifest: the Worker reads the parsed GitHub Issues CMS data from the
CONTENT_MANIFESTKV namespace. GitHub is only queried to bootstrap an empty namespace or refresh it after a webhook, so ordinary cache misses do not depend on GitHub availability. - Compact ideas list:
/api/listContent.jsonomits article bodies for the default/ideas, RSS, and sitemap paths. Full-body search remains available through/api/searchContent.json, which the browser downloads only after a reader uses the search box. - Instant publishing: a GitHub Issues webhook hits
/api/revalidate, which verifies the signature, refreshes the KV manifest, and rolls a KV-backed cache generation. Cache keys also include the Worker version, so both publishes and deploys bypass older edge entries. - Owned social cards: every public HTML page points at a versioned, 1200×630 PNG generated by
the same Worker under
/og/page/*or/og/article/*. Article cards are resolved from the trusted content manifest and can incorporate an explicit frontmatter image; query parameters never supply arbitrary card content. - Approximate read counts: public pages use a 0.5% engaged-read sample backed by D1. Each accepted sample adds the server-owned weight of 200, and a best-effort copy is sent to GA4 via Measurement Protocol. Selected older articles also have a static, explicitly approximate historical estimate that is added only when returning the public count.
- Ephemeral live readers: public pages optionally join a page-scoped, hibernating Durable Object room. Readers exchange only short-lived country, position, mode, reaction, and fixed share celebration frames. There is no identity, history, free-form chat, or per-reader presence database; D1 stores only aggregate hourly abuse and capacity counters.
Environment variables (Cloudflare Workers)
What each variable does
| Variable | Required? | What it does |
|---|---|---|
GH_TOKEN | Yes | A GitHub Personal Access Token used to authenticate calls to the GitHub Issues API (the CMS). Without it, requests are unauthenticated and capped at 60/hr, which the site blows through quickly and starts failing. With it, the limit is 5000/hr. Read at runtime via $env/dynamic/private (Cloudflare platform.env) and at build time for the prerendered pages — so it must be set in both the runtime secrets and the build environment. |
GH_WEBHOOK_SECRET | Recommended | A shared secret used to verify (HMAC SHA‑256) that incoming requests to /api/revalidate actually came from your GitHub webhook. This enables fast publishing: editing an Issue refreshes the KV manifest and rolls the cache generation instead of waiting for the s-maxage TTL. If unset, /api/revalidate returns 500 and you fall back to TTL-based freshness. |
GA4_MEASUREMENT_ID | Recommended | Public GA4 stream identifier used only by the server-side read-event mirror. Production currently uses G-TW6GTQ9Q4N and declares it as a non-secret [vars] value in wrangler.toml. |
GA4_API_SECRET | Production | Secret for the GA4 Measurement Protocol stream. It is sent only from the Worker and must never be committed, placed in a URL in source code, or exposed to the browser. The application treats it as optional so D1 counting survives a GA outage, but wrangler.toml requires it for production deployment. |
PODCAST_ADMIN_PASSWORD | Yes | Password for the private podcast studio. |
PODCAST_ADMIN_SESSION_SECRET | Yes | Signs private podcast-studio sessions. Rotate it to invalidate every existing session. |
PRESENCE_ENABLED | Recommended | Server-side emergency kill switch for new live-reader sockets. Set to false and deploy the main Worker to reject presence while leaving every page usable. |
PUBLIC_PRESENCE_ADMISSION_RATE | Recommended | Deployment-time browser admission fraction from 0 through 1. Production starts at 1; use 0.1 during a viral spike to reduce socket workload by roughly 90%. This value is public by design. |
Where to get the values
GH_TOKEN— GitHub → Settings → Developer settings → Personal access tokens. A classic token withpublic_repo(orrepofor private) scope is sufficient since it only reads Issues.GH_WEBHOOK_SECRET— generate any strong random string, e.g.openssl rand -hex 32. You'll paste the same value into the GitHub webhook config (below).GA4_API_SECRET— Google Analytics Admin → Data streams → select theswyx.ioweb stream → Measurement Protocol API secrets. The measurement ID and API secret are different values.
Set them on Cloudflare Workers
Option A — Dashboard: Cloudflare dashboard → Workers & Pages → your Worker → Settings → Variables and Secrets. Add each runtime value and encrypt secrets. GH_TOKEN must also be present in the Git-connected build environment because prerendered pages read it during builds.
Option B — Wrangler CLI:
# runtime secrets (encrypted)
npx wrangler secret put GH_TOKEN
npx wrangler secret put GH_WEBHOOK_SECRET
npx wrangler secret put GA4_API_SECRET
npx wrangler secret put PODCAST_ADMIN_PASSWORD
npx wrangler secret put PODCAST_ADMIN_SESSION_SECRET
Each command prompts for the value. List them with npx wrangler secret list.
wrangler.toml declares GH_TOKEN, both podcast-admin secrets, and GA4_API_SECRET as required so
deployments warn or fail clearly instead of silently publishing incomplete production
configuration.
It also declares the existing CONTENT_MANIFEST KV, READ_COUNTERS D1, and PODCAST_MEDIA R2
bindings. A fork or new Cloudflare account must create those resources first and replace their IDs
in wrangler.toml; Wrangler cannot recreate resources from another account's IDs.
The PRESENCE_ROOMS binding is different: it points to the separately deployed
swyxdotio-presence Worker. Deploy that Worker before the main site Worker.
Local Wrangler preview reads secrets from a gitignored
.dev.vars; ordinary Vite development can also use.env. Never copy production secret values into README,.env.example, tests, or shell history. A missingGA4_API_SECRETwarning during a local build is expected when analytics delivery is not under test.
Wire up the GitHub webhook (for instant publishing)
In your content repo: Settings → Webhooks → Add webhook:
- Payload URL:
https://swyxdotio.swyxio.workers.dev/api/revalidate - Content type:
application/json - Secret: the same value as
GH_WEBHOOK_SECRET - Events: "Let me select individual events" → check Issues only
On each Issue create/edit, the endpoint verifies the signature, refreshes the durable content
manifest, derives the affected slug, and rolls the cache generation for the relevant pages (/,
/ideas, /{slug}, /rss.xml, /sitemap.xml, and the list/api endpoints).
Cloudflare resources for a new environment
Create the durable resources before the first deployment, then copy the returned IDs into
wrangler.toml:
npx wrangler kv namespace create CONTENT_MANIFEST
npx wrangler d1 create swyxdotio-read-counters
npx wrangler r2 bucket create swyxdotio-podcast-media
The D1 binding must be named READ_COUNTERS, the database must use
migrations_dir = "migrations/read-counters", and migrations must be applied explicitly:
# Local development database
npx wrangler d1 migrations apply swyxdotio-read-counters --local
# Production database
npx wrangler d1 migrations apply swyxdotio-read-counters --remote
# Useful verification after migration or deployment
npx wrangler d1 execute swyxdotio-read-counters --remote \
--command "SELECT page_key, read_count, sample_count, sampling_policy_version FROM page_reads ORDER BY updated_at DESC LIMIT 20"
Migration 0002_add_sampling_metadata.sql contains tracked ALTER TABLE statements. Let Wrangler's
migration ledger apply it once; do not copy and execute those statements by hand or rerun the SQL
outside the migration command.
Do not seed historical estimates into D1. D1 is the independently auditable post-launch sample ledger; the historical estimates are a separate static presentation layer.
Live reader presence and sharing
Presence is intentionally playful and approximate. On desktop, admitted readers see ephemeral
country-labelled cursors. On mobile, the persistent representation is a flag bead on a reading
progress rail; a tap or drag adds a temporary passive touch cursor without interfering with native
scrolling. The only room communication is movement, one of 👋 ❤️ 💡 😂 ✨, and a fixed share
sparkle. Highlighted quote text, URLs, destinations, IP addresses, and user agents never enter the
Durable Object.
The browser waits until the page has been visible for two seconds before connecting. The feature is on by default and has a persistent Hide live readers preference. Hidden tabs stop sending; after 30 hidden seconds the socket closes. Rooms admit at most 32 readers, use WebSocket hibernation, and store no application data. Room IDs are resolved from the same finite public-page registry and persisted non-private article manifest used by read counts, so tools, APIs, feeds, errors, private articles, and arbitrary attacker-controlled keys cannot create rooms.
Deploy and develop
The Durable Object is an auxiliary Worker because the SvelteKit adapter owns the generated main
Worker entrypoint. Its declarative SQLite export lives in wrangler.presence.toml; SQLite is used
for the Durable Object class declaration only and the application performs no SQL writes.
# Local: build the SvelteKit Worker, then run both Workers together
npm run build
npm run preview:presence
# Production: this order is required for the external binding
npm run deploy:presence
npx wrangler deploy -c wrangler.toml
For a fast production smoke test, open the same public page in two normal browser contexts and
confirm the pill changes from 1 here to 2 here. Then verify hiding persists across reloads,
mobile emulation shows the reading rail, a reaction travels, a highlighted quote opens sharing,
and the browser network panel contains no selected text in WebSocket frames. An overflowed room
closes excess clients with 1013; malformed/abusive frames close with 1008.
Cost envelope and controls
The workload model assumes one admitted socket and an average of 12 compact incoming frames per eligible visit. Hibernated idle sockets are not billed for duration, outgoing WebSocket messages are free, and incoming messages are billed in 20-message request units. Under July 2026 Cloudflare Workers/Durable Objects paid pricing, the planning envelope is:
| Eligible views/day | Estimated total monthly workload cost |
|---|---|
| 25,000 | ~$5.03 |
| 1,000,000 | ~$18 |
| 5,000,000 | ~$92 |
| 10,000,000 | ~$187 |
The first row is mostly the existing $5 Workers plan. These are engineering estimates, not a bill forecast: visit length, motion, cache behavior, and other site Worker traffic can move the result. Do not add application heartbeats; they waste billable incoming messages and defeat hibernation.
Operational thresholds:
- Start with
PUBLIC_PRESENCE_ADMISSION_RATE = "1"andPRESENCE_ENABLED = "true". - If projected incremental presence cost exceeds $25/month or traffic approaches 1M eligible
views/day, set the build-time public admission value to
0.1, build, and deploy the main Worker:PUBLIC_PRESENCE_ADMISSION_RATE=0.1 npm run build && npx wrangler deploy -c wrangler.toml. Keep the matchingwrangler.tomlvalue as an operational record. Existing rooms remain useful while roughly 90% of browsers avoid opening a socket at all. - For emergency shutdown, set
PRESENCE_ENABLED = "false", rebuild with public admission0, then deploy the main Worker. Pages, read counts, selection sharing, and local confetti continue to work. - Monitor only aggregate Worker/DO requests, active duration,
room-full, malformed-frame, and rate-limit counts. Never add logs containing peer IDs, countries, coordinates, selections, or share destinations. Presence anomalies flush to D1 only at power-of-two checkpoints, bounding write amplification while keeping counts conservative between checkpoints.
Pricing references: Durable Objects pricing, WebSocket hibernation, and multi-Worker local development.
Open Graph image system
The site owns its social images rather than depending on Tailgraph or another hosted renderer:
@ethercorps/sveltekit-ogand its Vite plugin bundle Satori/resvg WASM for Cloudflare Workers.src/lib/social-meta.jsis the registry for the six public page cards and the shared metadata contract. ChangeOG_DESIGN_VERSIONwhenever a visual change should invalidate social caches.src/lib/og/contains card inputs, templates, rendering, committed open-licensed fonts, guarded explicit-image fetching, and the static total-failure fallback.- The committed fonts are Newsreader Semibold, Noto Sans Regular, and Caveat Semibold. The card template intentionally uses deterministic raw HTML/flex layout compatible with Satori; avoid introducing browser-only CSS or null template children without renderer tests.
/og/page/[key].png?v=<design-version>serves home, About, Ideas, Podcasts, Portfolio, and Subscribe cards./og/article/[slug].png?v=<updated-at>-<design-version>resolves only public articles from the persisted content manifest. Unknown, private, and malformed slugs return 404.- Article
image/cover_imagevalues may enhance the template. Only HTTPS JPEG, PNG, and WebP inputs up to 4 MB are accepted, with a 2.5-second fetch timeout. A bad image falls back to the no-image card rather than failing the request. - Generated PNGs are 1200×630, capped below 5 MB, and cached for one year as immutable. A complete
rendering failure returns
src/lib/og/assets/notebook-fallback.pngwithX-OG-Fallback: 1.
Every public page should use src/components/SocialMeta.svelte; do not add route-local duplicate
Open Graph tags. Non-article pages use og:type=website, articles use article, and all metadata
must contain absolute HTTPS URLs, dimensions, MIME type, and alt text. Tools, APIs, feeds, private
pages, and errors deliberately do not receive generated cards.
Useful production checks:
curl -I "https://swyx.io/og/page/home.png?v=1"
curl -I "https://swyx.io/og/article/learn-in-public.png?v=spotcheck"
curl -fsS "https://swyx.io/og/page/home.png?v=spotcheck" -o /tmp/swyx-og.png
file /tmp/swyx-og.png
Use a fresh version query when spot-checking so an older immutable edge entry cannot hide the new renderer. After deployment, also test a fresh X draft and LinkedIn Post Inspector; previously shared URLs may retain network-owned caches.
The focused unit suite validates registry coverage, metadata versioning, input rejection, image fetch bounds/timeouts, escaping, and Unicode. It does not currently rasterize and snapshot every PNG variant, so production byte/dimension checks and visual inspection remain required after OG template or font changes.
Read counts and GA4
The public counter is intentionally an order-of-magnitude estimate, not a precise analytics
system. The authoritative policy constants are in src/lib/read-counter.js; methodology and
historical backfill notes live in docs/read-counter.md.
Current policy (v1-p005):
- A browser must keep the page visible for 8 seconds; articles additionally require 25% scroll depth.
- A browser/page pair is deduplicated in local storage for 24 hours.
- Eligible reads are sampled at 0.5%. Only sampled clients POST; each accepted sample atomically
adds the server-owned weight of 200 and increments
sample_countin D1. - The API validates same-origin requests, rejects obvious bots and arbitrary/private content keys, and requires the expected sample-weight header. The client cannot choose the persisted weight.
- Unsampled engaged readers may GET the public total. The browser remembers the displayed count for 24 hours; the API is browser-cached for 5 minutes and edge-cached for 1 hour.
- Counts are visible by default. A reader can hide them globally from any displayed counter; the choice is stored locally and can be reversed with the adjacent “Show view count” control. Hiding the presentation does not disable anonymous counting or change GA privacy behavior.
- Successful D1 increments are mirrored asynchronously to GA4 as
engaged_read. GA failure, timeout, or missing configuration never affects the counter response and is never retried. - Global Privacy Control and Do Not Track suppress GA delivery and identifier creation. The GA payload uses a pseudonymous numeric client/session ID, denies advertising consent, and excludes IP, user agent, referrer, location, and user properties.
The production GA property is swyx - GA4 (property 391847479, web stream 5667734629,
measurement ID G-TW6GTQ9Q4N). Its event-scoped custom metric is:
- Name:
Estimated reads - Event parameter:
read_weight - Unit: Standard
GA4 is a secondary reporting mirror. D1 remains authoritative because Measurement Protocol delivery is best-effort and browser privacy/network behavior introduces systematic bias beyond the normal sampling error.
A separate monthly calibration Worker compares D1 sample deltas with GA4 delivery and stores a
diagnostic report. Its setup, interpretation limits, and production queries are documented in
docs/read-counter.md. In particular, its current session ratio is not an independent estimate of
historical traffic and must not be used to rewrite the static lifetime backfill.
Capacity and cost envelope
The 0.5% policy is deliberately bounded for an expected maximum of 10 million reads/day:
| Engaged reads/day | Sampled POSTs + D1 writes/day | Approx. daily 95% sampling error |
|---|---|---|
| 1,000,000 | 5,000 | ±2.77% |
| 5,000,000 | 25,000 | ±1.24% |
| 10,000,000 | 50,000 | ±0.87% |
This is sampling error only; blocked JavaScript, dropped requests, bots, and privacy choices can create larger systematic differences. At 10 million/day, sampled writes stay below D1 Free's 100,000 writes/day and use about 1.5 million writes/month, within the Workers Paid/D1 Paid included allowances as of July 2026.
The remaining scale risk is the public count GET, not D1: every unique browser/page/day can cause one Worker request even when the response is served from Cache API. At all-unique traffic, the incremental counter-endpoint envelope is roughly $11/month at 1M/day, $47/month at 5M/day, and $92/month at 10M/day under July 2026 Workers pricing, before other dynamic Worker traffic. If the site approaches that range, publish an hourly/daily static count snapshot through static assets or R2/custom-domain caching so readers no longer call the Worker counter endpoint. Do not increase D1 precision merely because traffic rises; rough magnitude is the product requirement.
Operational limitations
- A successful POST does not purge an already cached GET, so another reader may see a total that is stale for the one-hour shared TTL (or its stale-while-revalidate window). This is acceptable for an approximate counter.
- The browser records its 24-hour dedupe marker before sending the request. A transient failed POST can therefore suppress that browser's retry until the next day; this favors cost and duplicate resistance over perfect delivery.
- Same-origin, user-agent, finite-key, engagement, and weight checks are abuse friction rather than authentication. A custom client can forge browser headers, but it cannot create arbitrary D1 rows or choose a larger increment.
- D1 failure returns 503 from the counter API while the page itself remains usable; the component fails silently and omits the number. GA4 failure never changes a successful D1 response.
- Sitewide sampling converges quickly at scale, but individual long-tail pages can remain noisy for much longer. Do not present per-page totals as audited measurements.
Changing the sampling policy
Treat the rate, weight, and policy version as one migration:
- Change
READ_SAMPLE_RATE,READ_SAMPLE_WEIGHT, andREAD_SAMPLING_POLICYtogether. - Keep the server-owned header validation and D1 write weight aligned.
- Update the read-counter unit tests and
docs/read-counter.md. - Preserve existing rows; never rewrite old sampled counts as though they used the new policy.
- Recalculate the capacity table and GA custom-metric interpretation before deploying.
For a new GA stream, create a Measurement Protocol API secret in GA Admin, set the public
measurement ID in wrangler.toml, upload the secret with wrangler secret put GA4_API_SECRET, and
create the Estimated reads custom metric above. Validate a test event with Google's debug
Measurement Protocol endpoint before relying on Realtime.
Production read-counter checks:
# Public count and caching; the second request should become an edge HIT
spot="readme-spotcheck"
curl -i "https://swyx.io/api/reads/learn-in-public?spot=$spot"
curl -i "https://swyx.io/api/reads/learn-in-public?spot=$spot"
# Confirm deployed secret names without printing their values
npx wrangler secret list
Avoid casual production POST tests: every accepted sample intentionally adds 200 displayed reads. If one is necessary, record the before/after D1 values and use a real public key.
Deployment checklist
npm install
node --test tests/*.test.mjs
npm run check
npm run build
npx wrangler d1 migrations apply swyxdotio-read-counters --remote
npm run deploy:presence
npm run deploy:calibration
npm run deploy:monitor
npx wrangler deploy
After deployment:
- Record the Git commit and Worker Version ID.
- Fetch representative HTML, all six page-card endpoints, an article card with and without an explicit image, and at least one read-count endpoint using fresh query versions.
- Confirm PNG signatures/dimensions,
Cache-Control,Content-Type, and absence ofX-OG-Fallbackon normal renders. - Confirm the second identical read-count GET is an edge cache hit and POST responses remain
private, no-store. - Check D1 rows and GA Realtime
engaged_readindependently. Neither alone proves the other system is healthy. - If using the hourly monitor, verify that
ops_monitor_snapshotsreceives a fresh row and thatpresence_monitor_hourlystays aggregate-only. - Keep the worktree clean and never commit
.dev.vars, GA API secrets, or Cloudflare tokens.
Commands
npm run dev— local dev servernpm run build— production build (Cloudflare adapter)npm run preview— preview withwrangler dev; use this rather than plain Vite when testing local KV, D1, R2, Cache API, or Worker bindingsnpm run deploy:monitor— deploy the hourly read/presence monitor Workernode --test tests/*.test.mjs— fast unit and contract tests, including OG and read analyticsnode tests/markdown.test.mjs— markdown renderer regression checksnpm test— Playwright e2e (requires GH content)
Live URL
See https://swyx.io
- Netlify to Cloudflare DNS cutover notes
- https://sw-yx.js.org/ old site when learning to code.
- You can see previous iterations of the site from 2017 here: https://www.swyx.io/rewrite-2022
- The last version of the 2022 site was preserved at https://github.com/swyxio/swyxdotio2022
- The 2023 site is documented at https://www.swyx.io/rewrite-2023
Related repositories
Storybook is a frontend workshop maintained at storybookjs/storybook. It renders UI components and pages in isolation from your main application. Core renderers include React, Angular, Vue 3, Web Components, HTML, Ember, Svelte, and Preact, plus React Native and community support for Qwik and SolidJS. It's released under the MIT license.
Strapi is a self-hosted headless CMS that auto-generates REST and GraphQL APIs from content model definitions you build visually with the Content-Type Builder — no code required on the API side. It ships with authentication, granular roles and permissions, a media library, i18n, and draft/publish workflows out of the box, and the request pipeline (Routes → Middlewares → Controllers → Services) is fully overridable at each layer. TypeScript is first-class and you can pair it with SQLite for local dev or PostgreSQL/MySQL/MariaDB in production. The plugin system and customizable admin panel are real extensibility points, not marketing. The tradeoff: there's no official Docker image (you roll your own from your project), and major version upgrades have been historically painful — the migration docs are thorough but the process still demands attention. Worth it if you need full control over your content infrastructure and don't mind owning the deployment; less ideal if you want something you can set and forget.
Master programming by recreating your favorite technologies from scratch.
freeCodeCamp is a free, self-paced curriculum for learning to code, published as open source at freeCodeCamp/freeCodeCamp. It's a 501(c)(3) nonprofit funded by donor support, structured around six certifications in its Full-Stack Developer Curriculum, each gated by required projects instead of open-book quizzes. The repository also carries beta language certifications for developers, interview-prep resources, and the code that runs the live freecodecamp.org platform.
Quick answers
How does swyxio/swyxdotio compare to other Frontend projects?
swyxio/swyxdotio is tracked by TopGit in the Frontend category, with 413 GitHub stars and written in JavaScript. Browse the Frontend topic page on TopGit to compare it against similar projects by stars and activity.
How many stars does swyxio/swyxdotio have?
swyxio/swyxdotio has 413 GitHub stars — refresh the page for the live number, or check github.com/swyxio/swyxdotio. TopGit mirrors GitHub's count but does not claim minute-by-minute accuracy.
Is swyxio/swyxdotio open source?
Yes — swyxio/swyxdotio ships under the MIT license, which makes its source code freely readable (and, depending on license terms, forkable and reusable). Source: github.com/swyxio/swyxdotio.
What else is in the Frontend space?
swyxio/swyxdotio is tracked by TopGit under the Frontend category, alongside 6 GitHub-tagged topics. Trending and Topics pages list peer repositories of comparable stars and language.
What is swyxio/swyxdotio?
swyxio/swyxdotio (swyxio/swyxdotio) is a JavaScript project on GitHub. From the project's own README: This is the repo for swyx's blog - Blog content is created in github issues, then posted on swyx.io as blog pages! Comment/watch to follow along my blog within GitHub
Where can I see swyxio/swyxdotio in action?
The project maintains a homepage at https://swyx.io. The README tab on this page also usually contains screenshots and a quickstart.
Where do I read more about swyxio/swyxdotio?
This TopGit page is a snapshot — the READ ME tab shows the project's own README content (links stripped, images preserved). The GitHub repository at github.com/swyxio/swyxdotio is the definitive source.
Read full README in the tab above.
Want a second opinion on swyxdotio?
Ask an AI that can read this page — one click and you get its take on swyxdotio.