Self-hosted service
hyperlink-academy/leaflet avatar
hyperlink-academy/leaflet

Leaflet turns a database row into a Bluesky-backed publication with Replicache sync

A tool for shared writing and social publishing

525 stars35 forksTypeScriptMIT

At a glance

What is it?
Leaflet is a TypeScript web app for shared collaborative documents and social publishing, running on Next.js and Supabase with Replicache handling realtime sync and the publication layer built on the Bluesky protocol. It suits a team that wants a hosted writer with a following, and it needs Docker and a Supabase CLI to run locally.
Who is it for?
Leaflet is worth deploying if you want a writing tool where a document is a shareable page rather than a row in your own database, because the Leaflet and Publication concepts are the product and the Bluesky integration gives publications a real subscription model rather than a follower table you would have to build.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two objects, one for writing and one for publishing

The product is two things sharing one stack, and separating them is the fastest way to understand it. The first is a Leaflet, which the documentation describes as a simple shareable post or document with no account needed, created at leaflet.pub/new. The second is a Publication, which is like a blog or a newsletter that your friends can follow and that is built on Bluesky, created by connecting with Bluesky from leaflet.pub/home. The intended uses are given for both: shared notes and docs, collections, wikis and homepages for Leaflets, and blogs, newsletters, project logs or anything you want people to read and follow for Publications. The project runs its own example, Leaflet Lab Notes at lab.leaflet.pub. The interesting property is that the publishing side inherits a federated protocol rather than a follower table, which changes what a Publication can do.

The stack is Next.js, Supabase, Replicache and Tailwind

The technical details section lists five choices and each one is doing a specific job. TypeScript is used for types, React and Next.js for the UI and the app framework, Supabase for the database and storage layer, Replicache for the realtime data sync layer, and TailwindCSS for styling. Replicache is the load-bearing decision. It is a sync engine that keeps a client-side copy of the data the user is working with and reconciles with the server, which is what makes the collaborative editing in a Leaflet feel immediate rather than round-tripped through a save button. The Bluesky implementation lives in two directories the documentation points at, `lexicons` and `appview`, and the documentation notes that more is to come on how to bring your own frontend using the appview. The repository root shows the scale of the project, with directories for `app/`, `appview/`, `blueprint/`, `components/`, `contexts/`, `drizzle/`, `emails/`, `feeds/`, `lexicons/`, `lib/`, `mentions/`, `plans/`, `scripts/`, `specs/`, `stripe/`, `supabase/` and `skills-lock.json`, plus a `database.sql` and a `drizzle.config.ts`, so the database is managed with Drizzle rather than by hand.

Local development needs Docker because Supabase runs in containers

The prerequisites are short and specific. You need NodeJS version 20 or later, the Supabase CLI, and Docker, which is required for local Supabase. The installation starts by cloning the repository with `git clone https://tangled.org/leaflet.pub/leaflet.git`, with a note that on WSL it is recommended to install in the native file structure rather than a mounted Windows file structure, preferring `~/code/leaflet` over `/mnt/c/code/leaflet`. Then you install the dependencies with `npm install` and install the Supabase CLI, with a per-platform recipe for macOS, Windows, Linux, and the note that the CLI is already included in package.json so you can use `npx supabase` for commands. The manifest in the repository is consistent with that: the package is named minilink, the engines field requires node `>=22.13`, and the dev script is `TZ=UTC next dev --turbo -H 127.0.0.1`, so the development server binds to the loopback interface only.

The .env.local file is where the Supabase credentials land

Once the local Supabase stack is running, the environment file is filled in from its output. The example in the documentation is this:

env
# Supabase Configuration (from `supabase start` output)
NEXT_PUBLIC_SUPABASE_API_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-local-anon-key-from-terminal
SUPABASE_SERVICE_ROLE_KEY=your-local-service-role-key-from-terminal

# Database (default local connection)
DB_URL=postgresql://postgres:postgres@localhost:54322/postgres

# Leaflet specific
LEAFLET_APP_PASSWORD=any-password-you-want

The two Supabase keys come from the terminal output of `supabase start`, and the distinction between the anon key and the service role key matters because the service role key bypasses row level security. The `LEAFLET_APP_PASSWORD` is an application-level password you choose, and the value in the example says as much. The database URL uses the default local connection on port 54322, while the API URL uses 54321, which is the split you would expect between the Postgres port and the Supabase API port.

Migrations, Studio and the dev server are three commands

The database is brought up and migrated with the Supabase CLI rather than by hand. You start the local stack with `npx supabase start`, and the first run takes longer while Docker images download, and once complete you see connection details in the terminal output. For migrations there are two commands, `npx supabase db reset` for a first-time setup, which resets the database and applies all migrations, and `npx supabase migration up` to apply only the new ones, and the documentation notes that you do not need to link to a remote project for local development. You can then view the database in Supabase Studio at `http://localhost:54323`. The application starts with `npm run dev` and you visit `http://localhost:3000`. Stopping the local stack is `npx supabase stop`, and adding the `--no-backup` flag resets the database on the next start. The manifest has separate scripts for the unit and integration test suites, with `vitest run` for the full run and a variant excluding integration test files for the fast path.

The feed service and the appview are separate processes with their own scripts

Two optional services sit alongside the Next.js app, and the manifest shows how they are built and run. The feed service has a dev script that starts it with hot reloading on `http://localhost:3030`, and the documentation marks this step as unnecessary if you are not working on publication or Bluesky integration features. The appview is the Bluesky-facing service, built with esbuild into a single bundled Node file, and it has its own dev and prod scripts, with the dev one loading the environment through `--env-file='./.env.local'`. The repository also carries an `emails/` directory and an `email:dev` script, plus a `stripe/` directory, so email sending and payments are part of the product rather than afterthoughts. The appview being a separate service is what makes the bring your own frontend comment in the documentation plausible, since the protocol side can be consumed by a client that is not the Next.js app.

Two caching problems, both solved by deleting things

The troubleshooting section names two issues and both fixes are deletions. Persisting articles on a fresh install over a fresh database is usually due to stale Replicache entries, and the fix is to open the browser DevTools and delete the Replicache entries, usually under IndexedDB Storage. The second is that Supabase settings get cached in `.next`, so if you change where your Supabase connections point to you may need to delete the `.next` folder, which will rebuild the next time you start the app. Both are consequences of the same architectural choice. Replicache keeps client-side state on purpose, so it survives a server change that the client has not noticed yet, and Next.js caches build output on purpose, so a configuration change is not visible until the cache is dropped. The design is right for the collaborative case, and the cost is that a developer changing configuration under a running client has to know about two caches that are not mentioned in the error message.

Where a Bluesky-backed publishing tool is the wrong shape

Two limits are visible in the documentation. The first is that the publishing model is not yours to shape. A Publication is built on Bluesky, so a follower is an AT Protocol actor and a post is a record in a repository you do not host, and the repository carries the `lexicons/` directory, the `appview/` service, a `disco.json` file and scripts named `publish-lexicons` and `lexgen` that generate and publish the schema definitions. If your content needs to live in your own database, be private, or follow a subscription model you define, that whole layer is work rather than configuration. The alternative to weigh against is a conventional blog or notes application, where you own the posts table, the subscriber list and the delivery mechanism, and where the only thing you give up is a federated protocol and a ready-made social graph. The second limit is the runtime weight. Running the project locally means Docker for the database, a Next.js dev server, a feed service and an appview, and the manifest's script list is long enough to suggest a system with several moving parts. Leaflet earns its complexity if you want the federated publishing and the offline-tolerant collaborative editing, and it is heavier than it needs to be if you only want a place to write.

Editorial conclusion

Leaflet is worth deploying if you want a writing tool where a document is a shareable page rather than a row in your own database, because the Leaflet and Publication concepts are the product and the Bluesky integration gives publications a real subscription model rather than a follower table you would have to build. It is a poor fit if you need your own domain model, since the data layer is tied to Supabase and the document structures are defined as Lexicon schemas, and a poor fit if you want a self-contained stack, because the local development path assumes Docker for the local Supabase instance. Before you start, read the `lexicons` and `appview` directories to understand how the Bluesky side is implemented, and note that the package manifest requires Node 22.13 or later, while the local development prerequisites ask for Node 20 or later, and that the last push to the repository was on 2026-10-12.

Frequently asked questions

What is Leaflet and what can you build with it?

Leaflet is a tool for shared writing and social publishing. A Leaflet is an instant collaborative document with rich media and multiple pages that you can make with no account needed, and a Publication is a blog or newsletter that people can follow, built on Bluesky. The suggested uses include shared notes and docs, collections, wikis, homepages, blogs, newsletters and project logs.

How do I run Leaflet locally?

You need NodeJS version 20 or later, the Supabase CLI and Docker. Clone the repository, run npm install, start the local stack with npx supabase start, copy the .env example to .env.local with the values from that output, apply migrations with npx supabase db reset, then run npm run dev and open http://localhost:3000. You can also install the CLI with npx supabase since it is already in package.json.

What is Replicache used for in Leaflet?

Replicache is the realtime data sync layer of the stack, sitting alongside TypeScript for types, React and Next.js for the UI, Supabase for the database and storage layer, and TailwindCSS for styling. The documentation also notes that stale Replicache entries are the usual cause of articles failing to persist on a fresh install over a fresh database, and that they can be cleared from browser DevTools under IndexedDB Storage.

Do I need the feed service to develop Leaflet?

No. The documentation says the feed service setup step is not necessary if you are not working on publication or Bluesky integration features. It is started separately with npm run start-feed-service-dev, which runs it with hot reloading on http://localhost:3030.

Official sources

  1. hyperlink-academy/leaflet on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hyperlink-academy-leaflet.svg)](https://hysenlabs.com/projects/hyperlink-academy-leaflet)

Was this useful?