import { useState } from 'react' import { useStore } from '@nanostores/react' import { PageHeader, BillingPageSkeleton } from '../components/shared' import { Icons } from '../components/shared/Icons' import { Button } from '../components/ui' import { $billing } from '../stores/billing' import type { Tier, TierConfig } from '../types' type BillingCycle = 'monthly' | 'annual' function formatPrice(cents: number): string { return `$${(cents / 100).toFixed(0)}` } function getFeatureList(config: TierConfig, tier: Tier): { name: string; included: boolean }[] { if (tier === 'free') { return [ { name: 'Unlimited posts', included: true }, { name: 'Comments & reactions', included: true }, { name: 'writekit.dev subdomain', included: true }, { name: `${config.analytics_retention}-day analytics`, included: true }, { name: `API access (${config.api_rate_limit} req/hr)`, included: true }, { name: `${config.max_webhooks} webhooks`, included: true }, { name: `${config.max_plugins} plugins`, included: true }, { name: 'Custom domain', included: false }, { name: 'Remove "Powered by" badge', included: false }, ] } return [ { name: 'Unlimited posts', included: true }, { name: 'Comments & reactions', included: true }, { name: 'writekit.dev subdomain', included: true }, { name: 'Custom domain', included: true }, { name: 'No "Powered by" badge', included: true }, { name: `${config.analytics_retention}-day analytics`, included: true }, { name: `API access (${config.api_rate_limit} req/hr)`, included: true }, { name: `${config.max_webhooks} webhooks`, included: true }, { name: `${config.max_plugins} plugins`, included: true }, { name: 'Priority support', included: true }, ] } export default function BillingPage() { const { data: billing } = useStore($billing) const [billingCycle, setBillingCycle] = useState('annual') if (!billing) return const currentTier = billing.current_tier const currentConfig = billing.tiers[currentTier] const proConfig = billing.tiers.pro const proFeatures = getFeatureList(proConfig, 'pro') const annualSavings = (proConfig.monthly_price * 12 - proConfig.annual_price) / 100 return (
{/* Current Plan */}
Current Plan
{currentConfig.name} Active
{currentConfig.description}
{currentTier === 'free' && ( )}
{/* Upgrade Section */} {currentTier === 'free' && ( <>
Upgrade to Pro
Get custom domain, extended analytics, and more
{/* Billing Toggle */}
{/* Pro Plan Card */}
Pro
{formatPrice(billingCycle === 'monthly' ? proConfig.monthly_price : proConfig.annual_price)} /{billingCycle === 'monthly' ? 'mo' : 'yr'}
{billingCycle === 'annual' && (
{formatPrice(Math.round(proConfig.annual_price / 12))}/mo billed annually
)}
    {proFeatures.map((feature) => (
  • {feature.name}
  • ))}

Secure payment via Lemon Squeezy. Cancel anytime.

)} {/* Feature Comparison */}
Feature Comparison
Feature Free Pro
Custom domain
"Powered by WriteKit" badge Required Hidden
Analytics retention {billing.tiers.free.analytics_retention} days {billing.tiers.pro.analytics_retention} days
API rate limit {billing.tiers.free.api_rate_limit}/hr {billing.tiers.pro.api_rate_limit}/hr
Webhooks {billing.tiers.free.max_webhooks} {billing.tiers.pro.max_webhooks}
Plugins {billing.tiers.free.max_plugins} {billing.tiers.pro.max_plugins}
Posts Unlimited Unlimited
Support Community Priority
{/* FAQ */}
Questions
Can I cancel anytime?
Yes. Cancel anytime and keep access until the end of your billing period.
Can I switch from monthly to annual?
Yes. Switch anytime and we'll prorate your payment.
What happens to my content if I downgrade?
Your content stays. Custom domain will stop working, badge will appear, and analytics older than 7 days won't be accessible.
Can I export my data?
Yes. Export all your posts, settings, and assets anytime from the Data page. Your data is always yours.
) }