Skip to content
STACK IT FAST

Bluesky App

Curated OSSClassicMobile App20+ people

Audited from github.com/bluesky-social/social-app

Bluesky is the client application for the Bluesky social network: a single Expo/React Native codebase that ships to iOS, Android and web from one TypeScript source tree, backed by AT Protocol client libraries and a few small Go/Express services for web serving, link shortening and OG-card rendering.

Language
TypeScript
Database
PostgreSQL
Hosting
Docker
License
MIT
Running for
3 years 1 month
Team
20+ people

Why this architecture

One Expo/React Native codebase keeps three platforms in lockstep from shared UI and state code, while link shortening, OG-card rendering and web serving are split into small standalone Go/Express services so the client is not coupled to backend concerns it does not own.

Tech stack by layer

8 technologies · audited Sep 17, 2026
Frontend & UI
  • React NativeRenders the shared iOS/Android/Web UI from one src/ tree (App.tsx, App.web.tsx, Navigation.tsx).
  • ExpoManages native builds, autolinking and the dev client (expo 57, eas.json for EAS builds).
  • TypeScriptTypes the app and its native modules; separate tsconfig.check.*.json per platform gate CI type-checks.
  • TanStack QueryServer-state caching for feeds/profiles, persisted to disk via react-query-persist-client and an AsyncStorage persister.
  • TiptapRich-text post composer (mentions, hard breaks, placeholder extensions).
  • React NavigationStack and bottom-tab navigation (native-stack, bottom-tabs packages).(inferred)
Backend & APIs
  • GoThe bskyweb binary serves the built web bundle and the embedr/link HTML templates.
  • Expressbskylink (link resolver) and bskyogcard (Open Graph card renderer) are small standalone Express services.
Data & Persistence
  • PostgreSQLbskylink persists shortened links; accessed through Kysely's pg driver.
Infrastructure & Deploy
  • DockerRoot Dockerfile is a 3-stage pnpm → Go → debian-slim build for bskyweb; separate Dockerfiles exist for bskylink, bskyogcard and embedr.
  • GitHub ActionsCI (lint, typecheck, Go tests), nightly builds/E2E, and per-service AWS/GHCR image publishing.
  • SentryCrash reporting and sourcemap upload for both native (sentry-expo) and web builds.

Bluesky App architecture diagram

Open SVG
Bluesky App architecture diagramWeb app → bskyweb (load); iOS & Android apps → AT Protocol (@atproto/api); Web app → AT Protocol (@atproto/api); bskylink → PostgreSQL (Kysely); Shared short links → bskylink (resolve)CLIENTSSERVICESDATA & STORAGEEXTERNALiOS & Android appsReact Native · ExpoWeb appstatic bundleShared short linksbrowsersbskywebGo · serves web bundlebskylinkExpress · link resolverPostgreSQLshort links (Kysely)AT ProtocolAppView · PDSloadKyselyresolve@atproto/api@atproto/api
How the main components of Bluesky App connect, drawn from the audited repository.
Diagram as text
  • Web app (static bundle) → bskyweb (Go · serves web bundle): load
  • iOS & Android apps (React Native · Expo) → AT Protocol (AppView · PDS): @atproto/api
  • Web app (static bundle) → AT Protocol (AppView · PDS): @atproto/api
  • bskylink (Express · link resolver) → PostgreSQL (short links (Kysely)): Kysely
  • Shared short links (browsers) → bskylink (Express · link resolver): resolve

Key architectural decisions

5 decisions
  1. 01

    One repo builds native apps, a web SPA, and three small Go/Express services

    The workspace's src/ tree is the React Native app for iOS/Android/Web. Alongside it sit four largely independent projects with their own package.json and lockfile: bskyweb (Go, serves the web bundle and embed pages), bskylink (Express + PostgreSQL link shortener), bskyogcard (Express OG-card renderer), and bskyembed (a Preact micro-bundle for oEmbed cards).

  2. 02

    Web is not React Native Web running live — it is a static bundle served by a Go binary

    pnpm build-web runs expo export:web, then scripts/post-web-build.js copies the output into bskyweb/static and regenerates bskyweb/templates/scripts.html so Gos go:embed can bundle it. The root Dockerfile builds the JS with the official pnpm image before compiling bskyweb` with Go 1.26 in a second stage.

  3. 03

    Native dependency patches are pinned and reviewable, not forked silently

    pnpm-workspace.yaml lists 17 patchedDependencies entries (react-native, expo, expo-notifications, react-native-screens, etc.), each paired with a .patch file under patches/ and often a companion .patch.md explaining why the patch exists.

  4. 04

    Server state and offline caching go through TanStack Query, not a custom store

    package.json depends on @tanstack/react-query, react-query-persist-client and query-async-storage-persister together, so cached feed and profile data survives app restarts without a bespoke local database layer.

  5. 05

    Feature flags run through GrowthBook rather than a homegrown system

    @growthbook/growthbook and @growthbook/growthbook-react are direct dependencies, giving the app remotely configurable flags and experiments independent of the AT Protocol client packages.

How Bluesky App is built

How Bluesky is structured

The repository is a pnpm workspace whose main product is the src/ React Native app, entered from App.tsx (native) and App.web.tsx (web), with routing defined in Navigation.tsx and routes.ts. Around that app sit several sibling projects, each with its own package.json and lockfile: bskyweb (Go, serves the production web bundle and the embedr/link HTML templates), bskylink (Express + PostgreSQL, resolves go.bsky.app-style short links), bskyogcard (Express, renders Open Graph preview images with Satori), bskyembed (a small Preact/Vite bundle for embeddable post widgets), and dev-env (a local mock server for development). Platform-specific native code lives in modules/ (Expo config plugins and native view modules for bottom sheets, GIF playback, background notifications) rather than inside src/.

Frontend

The UI is React Native, run through Expo's managed workflow (expo 57, eas.json for EAS builds, app.config.js). Navigation uses React Navigation's native-stack and bottom-tabs. Server state is handled by TanStack Query, paired with @tanstack/query-async-storage-persister so cached feed and profile data survives restarts without a separate local database. The post composer uses Tiptap (@tiptap/core plus mention, placeholder and hard-break extensions). Internationalization runs on Lingui, with lingui.config.ts driving extraction/compilation and Crowdin handling translator workflows. Feature flags and experiments are GrowthBook (@growthbook/growthbook-react), and crash/error reporting goes through Sentry via sentry-expo and @sentry/react-native. Styling and layout live under src/alf/ (an internal design-system layer referenced throughout src/components and src/view).

Backend & APIs

This repository does not implement Bluesky's social-graph backend — that lives in the separate atproto repository referenced from the README. What it does own are three small services. bskyweb is written in Go (bskyweb/go.mod depends on the indigo AT Protocol Go libraries, echo for routing and pongo2 for templates) and serves the static web build plus embed/link landing pages. bskylink and bskyogcard are both Express services: bskylink resolves short links and logs metrics via prom-client; bskyogcard renders PNG Open Graph cards for posts using satori and @resvg/resvg-js, fetching post data through @atproto/api.

Data & persistence

The client itself holds no server-side database — it talks to AT Protocol Personal Data Servers and the Bluesky AppView through @atproto/api and @bsky/sdk, with local caching via TanStack Query's persisted cache. The one service with its own datastore is bskylink, which uses Kysely's PostgreSQL driver (pg) plus an lru-cache layer for hot lookups.

Build, test & deploy

Builds run through Expo/EAS (eas build, use-build-number-with-bump scripts) for native binaries, and expo export:web plus post-web-build.js for the web bundle that bskyweb embeds. Tests use Jest (test, test-ci with jest-junit reporting) for unit tests and Maestro (__e2e__/*.yml) for end-to-end and performance flows, with a flashlight-based performance measurement pipeline. GitHub Actions workflows cover linting (lint.yml, oxlint), Go tests (golang-test-lint.yml), nightly builds and E2E (nightly-build.yml, nightly-e2e.yml), and per-service image publishing to AWS/GHCR (build-and-push-bskyweb-aws.yaml, build-and-push-bskyweb-ghcr.yaml, and equivalents for embedr, link and ogcard).

Self-hosting notes

The root Dockerfile builds bskyweb end to end: an official pnpm image compiles the web bundle and runs pnpm intl:build, then a golang:1.26-bookworm stage compiles the Go binary with the web assets embedded via go:embed, and a debian:bookworm-slim runtime stage runs it under dumb-init. bskylink, bskyogcard and the embedr build each have their own Dockerfiles for independent deployment. There is no single docker-compose file tying these services together in this repository; each is meant to be deployed and scaled separately.

What to copy (and what not to)

The split between a portable, framework-owned React Native app and small, independently deployable satellite services (link shortener, OG-card renderer) is a useful pattern for keeping a client codebase decoupled from ancillary backend concerns. The explicit, per-package patchedDependencies list in pnpm-workspace.yaml — with a .patch.md rationale file next to most patches — is a good template for tracking deviations from upstream native dependencies without silent forks. Be more cautious about copying the licensing model wholesale: the MIT license covers the source code but explicitly excludes several icons, fonts and the landing illustration, per ASSETS.md, so a fork needs its own audit before reusing assets.

Sources & repo audit

Audited
Sep 17, 2026
Commit
547b615
License
MIT

Independent analysis of repository at github.com/bluesky-social/social-app. Spotted an inaccuracy? Use the claim form to request a correction.

Show this stack on your README or website
Tech stack of Bluesky App
[![Tech stack of Bluesky App on STACK IT FAST](https://stackitfast.com/badge/bluesky-app.svg)](https://stackitfast.com/project/bluesky-app)

Paste it into your README. It shows your current stack and links to this page.

Use this stack

Scaffold it with your agent

Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with Bluesky App's architecture.

  1. 1Copy the promptThe full markdown spec, with every layer and decision.
  2. 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
  3. 3Paste and scaffoldUse it as the first instruction; review before you ship.
use-this-stack.md · 59 lines · 7.4 KB
# MISSION: Scaffold "Bluesky App" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **Bluesky App**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: Bluesky App
- **What It Does**: Bluesky is the client application for the Bluesky social network: a single Expo/React Native codebase that ships to iOS, Android and web from one TypeScript source tree, backed by AT Protocol client libraries and a few small Go/Express services for web serving, link shortening and OG-card rendering.
- **Domain & Category**: Mobile App
- **Production Scale**: 20+ people
- **Development Mode**: CLASSIC
- **Architectural Rationale**: One Expo/React Native codebase keeps three platforms in lockstep from shared UI and state code, while link shortening, OG-card rendering and web serving are split into small standalone Go/Express services so the client is not coupled to backend concerns it does not own.
- **Live Website Reference**: https://bsky.app
- **Source Repository**: https://github.com/bluesky-social/social-app
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: TypeScript, React Native, Expo, Go, TanStack Query, Express, PostgreSQL, Docker
- **Primary Language(s)**: TypeScript, Go, JavaScript, Kotlin, Swift
- **License of the reference repo**: MIT
- **Frontend**: React Native — Renders the shared iOS/Android/Web UI from one src/ tree (App.tsx, App.web.tsx, Navigation.tsx).; Expo — Manages native builds, autolinking and the dev client (expo 57, eas.json for EAS builds).; TypeScript — Types the app and its native modules; separate tsconfig.check.*.json per platform gate CI type-checks.; TanStack Query — Server-state caching for feeds/profiles, persisted to disk via react-query-persist-client and an AsyncStorage persister.; Tiptap — Rich-text post composer (mentions, hard breaks, placeholder extensions).; React Navigation — Stack and bottom-tab navigation (native-stack, bottom-tabs packages).
- **Backend & APIs**: Go — The bskyweb binary serves the built web bundle and the embedr/link HTML templates.; Express — bskylink (link resolver) and bskyogcard (Open Graph card renderer) are small standalone Express services.
- **Data & persistence**: PostgreSQL — bskylink persists shortened links; accessed through Kysely's pg driver.
- **Infrastructure & deploy**: Docker — Root Dockerfile is a 3-stage pnpm → Go → debian-slim build for bskyweb; separate Dockerfiles exist for bskylink, bskyogcard and embedr.; GitHub Actions — CI (lint, typecheck, Go tests), nightly builds/E2E, and per-service AWS/GHCR image publishing.; Sentry — Crash reporting and sourcemap upload for both native (sentry-expo) and web builds.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/bluesky-social/social-app @ 547b615)
1. **One repo builds native apps, a web SPA, and three small Go/Express services**: The workspace's src/ tree is the React Native app for iOS/Android/Web. Alongside it sit four largely independent projects with their own package.json and lockfile: bskyweb (Go, serves the web bundle and embed pages), bskylink (Express + PostgreSQL link shortener), bskyogcard (Express OG-card renderer), and bskyembed (a Preact micro-bundle for oEmbed cards).
2. **Web is not React Native Web running live — it is a static bundle served by a Go binary**: `pnpm build-web` runs `expo export:web`, then `scripts/post-web-build.js` copies the output into `bskyweb/static` and regenerates `bskyweb/templates/scripts.html` so Go`s `go:embed` can bundle it. The root Dockerfile builds the JS with the official pnpm image before compiling `bskyweb` with Go 1.26 in a second stage.
3. **Native dependency patches are pinned and reviewable, not forked silently**: `pnpm-workspace.yaml` lists 17 `patchedDependencies` entries (react-native, expo, expo-notifications, react-native-screens, etc.), each paired with a `.patch` file under `patches/` and often a companion `.patch.md` explaining why the patch exists.
4. **Server state and offline caching go through TanStack Query, not a custom store**: package.json depends on `@tanstack/react-query`, `react-query-persist-client` and `query-async-storage-persister` together, so cached feed and profile data survives app restarts without a bespoke local database layer.
5. **Feature flags run through GrowthBook rather than a homegrown system**: `@growthbook/growthbook` and `@growthbook/growthbook-react` are direct dependencies, giving the app remotely configurable flags and experiments independent of the AT Protocol client packages.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
   - Structure as a Turborepo monorepo with strict package boundaries:
     - `apps/web`: Application UI, routing, layouts, and server endpoints.
     - `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
     - `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
     - `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
   - Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
   - Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
   - Utilize Expo Router v4 with file-based navigation. Apply NativeWind for cross-platform styling and wrap screens with SafeAreaProvider for native iOS and Android gesture and notch support.
4. **Design System & Aesthetics**:
   - Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
   - Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
   - Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
   - Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (PostgreSQL): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.
Scaffolded something with this prompt?
Would you pick this stack for a mobile app project?

Frequently asked about Bluesky App

What is the Bluesky app built with?

The client is a single TypeScript/React Native codebase managed by Expo that targets iOS, Android and web from one src/ tree. The repository also contains small satellite services in Go and Express: bskyweb serves the built web bundle, bskylink resolves short links against PostgreSQL via Kysely, and bskyogcard renders Open Graph preview images.

Does Bluesky's web app run React Native Web directly in production?

No. pnpm build-web exports a static bundle with Expo, then post-web-build.js copies it into bskyweb/static, and a Go binary (bskyweb) serves that bundle. The Dockerfile builds the JS with pnpm before compiling the Go server around it in a second stage.

Can I self-host or fork this app?

The README grants permission to fork, but ASSETS.md lists icons, fonts and the landing-screen illustration that are licensed to Bluesky alone and are not covered by the repository's MIT license, so forks must replace those assets and rebrand before shipping.

Does the app talk to AT Protocol servers directly, or through its own backend?

The app depends on @atproto/api, @bsky/sdk and related AT Protocol packages to reach Bluesky's Personal Data Servers and AppView directly. This repository's own backend services (bskylink, bskyogcard, bskyweb) handle link shortening, OG cards and serving the web build, not the social graph itself.

How is internationalization handled?

Through Lingui: lingui.config.ts plus intl:extract and intl:compile scripts generate message catalogs, and translations are synced through Crowdin (crowdin.yml, a nightly-update-source-languages workflow).

One email a month: new deep dives and stack trends

New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.

use-this-stack.md