A modern web app for composing, managing, and exporting Twitter/X threads with cloud sync, image uploads, and shareable draft links.
- Multi-post threads - Compose threads with unlimited posts
- Rich content - Add up to 4 images per post
- Emoji picker - Insert emojis directly into posts
- Tweet embeds - Embed existing tweets in your thread
- Character counter - Real-time 280-character limit tracking
- Thread numbering - Automatic post numbering (1/n format)
- Cloud sync - Auto-save drafts to Supabase
- Offline editing - Continue editing without internet (IndexedDB)
- Conflict resolution - Latest timestamp wins on sync
- Published archive - Mark drafts as published to archive them
- Shareable links - Anyone with the link can view drafts (read-only)
- JSON export - Download drafts for backup
- Markdown export - Export threads with formatting
- Copy to clipboard - With or without numbering
- PWA support - Install as mobile app
- Mobile responsive - Works on all devices
- GitHub OAuth - Simple authentication
- Draft management - List, edit, and delete drafts
- Frontend: React 18 + Vite
- Backend: Supabase (PostgreSQL + Auth + Storage)
- Deployment: Vercel
- Styling: Custom CSS
- PWA: vite-plugin-pwa
- Click the "Deploy with Vercel" button above
- Follow SETUP.md for complete instructions
- You'll need:
- Supabase account (free)
- GitHub account (for OAuth)
-
Clone the repository:
git clone https://github.com/YOUR_USERNAME/x-thread-draft-tool cd x-thread-draft-tool -
Install dependencies:
npm install
-
Create
.env.local:VITE_SUPABASE_URL=your_supabase_url VITE_SUPABASE_ANON_KEY=your_supabase_anon_key -
Run development server:
npm run dev
.
├── supabase/
│ └── schema.sql # Database schema
├── src/
│ ├── components/ # React components
│ │ ├── PostBox.jsx # Individual post editor
│ │ ├── EmojiPicker.jsx
│ │ ├── ImageUpload.jsx
│ │ ├── TweetEmbed.jsx
│ │ ├── LoginPage.jsx
│ │ ├── DraftList.jsx
│ │ └── ...
│ ├── pages/
│ │ └── DraftEditor.jsx # Main editing page
│ ├── utils/
│ │ ├── supabase.js # Supabase client
│ │ └── ...
│ ├── styles/
│ │ └── App.css # Styles
│ └── App.jsx # Router setup
├── vercel.json # Vercel configuration
└── vite.config.js # Vite + PWA config
The app uses three main tables in Supabase:
id- UUID (primary key)user_id- UUID (foreign key to auth.users)title- Textposts- JSONB (array of post objects)is_published- Booleanpublished_at- Timestampcreated_at- Timestampupdated_at- Timestamp
{
"id": "uuid",
"text": "Post content",
"images": ["url1", "url2"],
"embeddedTweet": "https://x.com/user/status/123"
}Each draft has a unique URL (/draft/:id) that can be shared. Anyone with the link can view the draft, but only the owner can edit it.
Mark a draft as "published" to:
- Move it to the Published tab
- Delete all uploaded images (to save storage)
- Archive the draft (read-only for owner too)
The app works offline using:
- Service Worker (PWA)
- IndexedDB for draft caching
- Auto-sync when back online
- Drag & drop or click to upload
- Automatic compression (max 1920px width)
- 5MB per image limit
- 4 images per post max
- Stored in Supabase Storage
VITE_SUPABASE_URL- Your Supabase project URLVITE_SUPABASE_ANON_KEY- Your Supabase anon/public key
See SETUP.md for detailed setup instructions.
The app can run entirely for free:
- Vercel: Free for personal projects
- Supabase Free Tier:
- 500MB database
- 1GB storage
- 50K monthly active users
Unless you have thousands of users, you'll stay within free limits.
Contributions are welcome! Please open an issue or PR.
MIT
Built with React, Supabase, and Vercel.