Storyie: a diary app that ships on web, iOS, and Android

Storyie Engineering Team
6 min read

An introduction to Storyie — what it does, how the privacy model works, and why we built a pnpm monorepo with Lexical, Supabase, and SST to make one team ship across three platforms.

We built Storyie because no existing tool handled both sides of a diary: the private, write-for-yourself side and the occasional "this is worth sharing publicly" side. Existing options were either completely private with no path to the web, or blog-first with privacy bolted on as an afterthought.

Storyie flips the default. Every entry is private until you decide otherwise. Going public is a single toggle, not a migration.

This post covers what Storyie does, how the plan tiers break down, and a brief look at the tech choices behind it.

TL;DR

  • Private by default, public on demand — per-entry visibility toggle, not a global setting.
  • Shared Lexical editor across web and iOS. A diary written on one opens correctly on the other.
  • Supabase Row Level Security enforces privacy at the database layer, not just the application layer.
  • pnpm monorepo: Next.js for web, Expo for iOS/Android, shared packages for the editor, subscription logic, and database schema.

Platform

Status

Web

Live at storyie.com

iOS

App Store

Android

Beta (email registration)

What Storyie is

Storyie is a diary app where writing is the primary action, and publishing is optional. The core flow: open the app, write, close the app. Everything stays private. On the days you write something you'd want others to read, flip the toggle.

That's the product in one sentence. Below is how each piece of it works.

Visibility is per-entry, not per-account

A diary entry can be private or public independently. You don't put yourself in "public mode" or "journal mode" — both live in the same list, same interface. The distinction is just a toggle on each entry.

Private entries are never indexed, never in sitemaps, never accessible without your authenticated session. Public entries get a stable URL, Open Graph metadata, and JSON-LD Article structured data. The switch from private to public is immediate; switching back is equally immediate.

We enforce this at the Supabase row level, not just in application code. Row Level Security policies prevent the API from returning private entries to unauthenticated or unauthorized callers regardless of what the application layer does. That's the part we were most intentional about — application-layer auth is easy to accidentally bypass; database-layer auth is not.

The editor is shared across web and iOS

Every entry uses a Lexical-based rich-text editor. Headings, bold, italic, lists, links, inline code, blockquotes, and images all work on both web and mobile.

The cross-platform part is not just "similar feature sets" — it's the same serialization format. An entry written on the web is stored as a JSON blob (SerializedEditorState). The iOS app parses that exact JSON and renders it through the same node definitions. If you wrote a diary with a heading, a bulleted list, and an inline image on the web, you'll see that heading, list, and image when you open the entry on iOS.

We wrote a longer post about the architecture behind this: Cross-platform Lexical with use dom.

Public entries become web pages

When an entry is public, it's accessible at storyie.com/u/{username}/diary/{slug}. That page is server-rendered, responsive, and includes structured metadata for search engines. It's not a preview mode or an export — it's the canonical page.

Pro subscribers get a personal subdomain (username.storyie.com). The content is still managed in Storyie; the subdomain just makes the URL look like a standalone site.

Notes

Diary entries are date-anchored. Notes are not. Notes are for ideas, running lists, anything that doesn't belong to a particular day. They use the same editor, and they're private-only for now — no public sharing for notes yet.

AI Writing Assist (coming soon)

We're building an AI assistant backed by the Claude API. The goal is prompts for writing something when you don't know where to start, light editing suggestions, and summarization. It's not in production yet, but it's in active development.

Plans

Feature

Free

Pro

Diary entries per month

31

Unlimited

Notes

100

Unlimited

Images per entry

1

10

AI Writing Assist

✓ (coming)

✓ (coming)

Cross-platform sync

Personal subdomain

The free plan is 31 entries per month — one per day — which covers most regular journaling. Pro is for heavier writers and anyone who wants the subdomain or higher image counts.

How it's built

Storyie is a pnpm monorepo with two apps and a set of shared packages:

storyie/
├── apps/
│   ├── web/          # Next.js 16, App Router, SST → AWS
│   └── expo/         # Expo SDK 55, React Native 0.83
└── packages/
    ├── lexical-common/    # cross-platform editor core
    ├── lexical-editor/    # web-specific editor (DOM nodes, SSR)
    ├── database/          # Drizzle ORM schemas + Supabase migrations
    └── subscription/      # plan limits, entitlement logic

Why a monorepo

The editor and the subscription logic need to be the same on both platforms. If we let the web and mobile apps define their own Lexical node types independently, an entry written with a HeadingNode on web would silently corrupt when parsed on mobile (Lexical drops unknown nodes on parse). A shared package with a single node definition set prevents that class of bug entirely.

Same rationale for @storyie/subscription: the rules for what Free vs. Pro users can do shouldn't be reimplemented in two codebases.

Why Supabase

Row Level Security. We wanted privacy enforcement at the database layer. Supabase's RLS lets us write policies like "only the author can read private entries, anyone can read public entries" as actual PostgreSQL policy — not application middleware. Even a bug in the Next.js API layer can't leak a private entry if the database won't return it.

Auth is also through Supabase (Google and Apple OAuth), and we use Supabase Storage for diary images.

Why SST for web deployment

SST v3 handles the Next.js App Router deployment to AWS without us having to manage infrastructure manually. Serverless means the cost scales with usage, which matters for a product that's not yet at high traffic. The deployment pipeline is a sst deploy in CI.

Payments

Web subscriptions use Stripe. Mobile subscriptions use RevenueCat, which wraps the App Store and Play Store billing. Both update the same user record in Supabase. The @storyie/subscription package handles the cross-platform entitlement logic so neither app reimplements it.

What's next

  • Comment threads on public entries
  • Better tag and category management
  • Usage and writing statistics dashboard
  • Android out of beta on the Play Store

Try it

The web app is at storyie.com. The iOS app is on the App Store. Android beta signups are at storyie.com/android-beta.

Related Posts

Try Storyie

Write a diary at storyie.com, then open the same entry on the iOS app. Same content, same formatting, same custom node types — the platform boundary is invisible from the outside.