Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/betterspacx/app/llms.txt

Use this file to discover all available pages before exploring further.

Betterflow uses two complementary storage layers that work independently of each other. Cloudflare R2 is a remote object store for assets that need to be served publicly — background images and videos recorded by the Chrome extension. Browser storage (IndexedDB and LocalStorage) handles all user-uploaded images, export history, export preferences, and canvas state, so the editor stays fully functional even without an R2 bucket configured.

Cloudflare R2

R2 is Cloudflare’s S3-compatible object storage. Betterflow uses it for two specific workloads:
  • Hosting the built-in background image gallery assets served to the editor UI.
  • Receiving Chrome extension video uploads via presigned URLs generated by the /api/upload-url route.

Setup

1

Create a Cloudflare account and R2 bucket

Sign up at cloudflare.com if you don’t have an account. In the Cloudflare dashboard navigate to R2 Object Storage → Create bucket. Give your bucket a name (e.g. betterflow-storage) and choose the storage region closest to your users.Note your Cloudflare Account ID — it appears in the right-hand sidebar on any dashboard page and is required for the S3-compatible endpoint.
2

Generate R2 API credentials

In the Cloudflare dashboard go to R2 → Manage API Tokens and create a new token with Object Read & Write permissions scoped to your bucket. Copy both values immediately — the secret is only shown once.
CredentialEnvironment variable
Access Key IDR2_ACCESS_KEY_ID
Secret Access KeyR2_SECRET_ACCESS_KEY
API TokenR2_API_TOKEN
Account IDCLOUDFLARE_ACCOUNT_ID
Bucket nameR2_BUCKET_NAME
R2_ACCESS_KEY_ID=your_access_key_id
R2_SECRET_ACCESS_KEY=your_secret_access_key
R2_API_TOKEN=your_r2_api_token
CLOUDFLARE_ACCOUNT_ID=your_cloudflare_account_id
R2_BUCKET_NAME=betterflow-storage
3

Configure CORS on the R2 bucket

The browser needs to fetch assets cross-origin. In the Cloudflare dashboard open your bucket, go to Settings → CORS Policy, and add a rule that allows GET requests from your site’s origin:
[
  {
    "AllowedOrigins": ["https://yourdomain.com"],
    "AllowedMethods": ["GET", "PUT"],
    "AllowedHeaders": ["*"],
    "MaxAgeSeconds": 3600
  }
]
Using "AllowedOrigins": ["*"] is convenient during local development but should be restricted to your production domain before going live.
4

Set the public URL environment variables

Betterflow’s next.config.ts reads NEXT_PUBLIC_R2_PUBLIC_URL and NEXT_PUBLIC_R2_CUSTOM_DOMAIN to allowlist hostnames for Next.js Image Optimization. Set both to the correct public endpoint for your bucket:
# R2 default public bucket URL (e.g. https://pub-xxxx.r2.dev)
NEXT_PUBLIC_R2_PUBLIC_URL=https://pub-xxxx.r2.dev

# Custom domain mapped to R2 (e.g. assets.yourdomain.com)
NEXT_PUBLIC_R2_CUSTOM_DOMAIN=assets.yourdomain.com

# CDN base URL used when building public file paths after upload
NEXT_PUBLIC_CDN_URL=https://cdn.yourdomain.com
After updating .env, restart the development server (pnpm run dev) so the new values are picked up by next.config.ts at startup.
5

(Optional) Map a custom domain to your bucket

For cleaner public URLs and improved cache-hit ratios, map a custom subdomain to your R2 bucket. In the Cloudflare dashboard open your bucket → Settings → Custom Domains and add a domain you control (e.g. assets.yourdomain.com). Cloudflare automatically provisions a TLS certificate.Update NEXT_PUBLIC_R2_CUSTOM_DOMAIN and NEXT_PUBLIC_CDN_URL to use the new domain and redeploy.
Custom domains also let you serve assets through Cloudflare’s global CDN, reducing latency for background image loads worldwide.

Browser Storage

Betterflow uses two browser-native storage mechanisms — IndexedDB for binary blobs and structured records, and LocalStorage for lightweight key/value state. Both require no configuration and work automatically across sessions.

IndexedDB Stores

IndexedDB is used for all data that is too large or too structured for LocalStorage: uploaded image blobs, exported image files, and export preferences.
StoreKeyContents
image-blobsUnique image IDUploaded image blob, MIME type, and timestamp. A blob URL is created from this record for immediate canvas rendering and persisted here so images survive page refreshes.
exportsUnique export IDExported image blob together with format, quality, scale, timestamp, and file name metadata.
export-preferences'preferences'Last-used export settings: format (PNG / MP4 / WebM / GIF), quality preset, and output scale. Restored when the export panel opens.

LocalStorage Keys

LocalStorage holds lightweight editor state that is fast to read synchronously on startup.
KeyContents
canvas-objectsSerialized canvas object state (image transforms, overlays, borders, shadows, 3D perspective settings). Restored automatically when the editor loads.
canvas-background-prefsBackground preferences (gradient colors, solid color, background image selection, blur and noise settings).
No configuration is required — all IndexedDB and LocalStorage reads and writes happen automatically in the browser. Data persists across sessions and survives page refreshes as long as the browser’s storage is not cleared.
Browser storage is scoped to the origin, so data stored at localhost:3000 is separate from data at your production domain. Users who clear their browser storage or switch to a different device will start with a blank canvas.

Build docs developers (and LLMs) love