From bb526b7ba9dc59d9e2d579e1758b086866ea2188 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:56:48 -0700 Subject: [PATCH] Add pricing more prominently --- src/app/pricing/page.jsx | 275 +++++++++++++++++++++++++++ src/components/Header.jsx | 2 +- src/components/PricingCalculator.jsx | 183 ++++++++++++++++++ 3 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 src/app/pricing/page.jsx create mode 100644 src/components/PricingCalculator.jsx diff --git a/src/app/pricing/page.jsx b/src/app/pricing/page.jsx new file mode 100644 index 00000000..7d042212 --- /dev/null +++ b/src/app/pricing/page.jsx @@ -0,0 +1,275 @@ +import { Fragment } from 'react' +import { HeaderSparse } from '@/components/HeaderSparse' +import { FooterMarketing } from '@/components/FooterMarketing' +import { PricingCalculator } from '@/components/PricingCalculator' +import Link from 'next/link' +import { Check, Minus, Activity, BarChart3, Server, Headphones } from 'lucide-react' + +export const metadata = { + title: 'Pricing | Iroh', + description: 'Plans and pricing for iroh services. Free for development, Pro for production, Enterprise for large-scale deployments.', +} + +const SERVICES_URL = 'https://services.iroh.computer' +const CAL_URL = 'https://cal.com/team/number-0/iroh-services' + +const plans = [ + { + name: 'Free', + description: 'For local development and testing.', + price: '$0', + period: '/month', + href: `${SERVICES_URL}?utm_source=website&utm_content=pricing-free`, + buttonLabel: 'Get Started', + features: [ + 'All features in Pro, limited', + '1 day retention', + 'Community support', + ], + }, + { + name: 'Pro', + description: 'For shipping your app to prod.', + price: '$19', + period: '/month', + popular: true, + href: `${SERVICES_URL}?utm_source=website&utm_content=pricing-pro`, + buttonLabel: 'Free trial', + features: [ + 'Pay as you go pricing', + '7 day retention', + '8x5 support tickets', + ], + }, + { + name: 'Enterprise', + description: 'For large-scale deployments.', + price: 'Contact Us', + period: '', + href: CAL_URL, + buttonLabel: "Let's Chat", + features: [ + 'On-prem and multi-cloud', + 'Custom retention', + 'SLAs', + 'Support Engineer', + ], + }, +] + +const sectionIcons = { + Platform: Activity, + Metrics: BarChart3, + Hosting: Server, + Support: Headphones, +} + +const featureSections = [ + { + name: 'Platform', + features: [ + { name: 'Collaborators', free: 'One user', pro: 'Unlimited users', enterprise: 'Unlimited users' }, + { name: 'Network Diagnostics', free: '3 reports', pro: '10 reports', proNote: 'then pay as you go', enterprise: 'Custom', enterpriseNote: 'Volume discounts' }, + ], + }, + { + name: 'Metrics', + features: [ + { name: 'Data Points per Minute', free: '1K DPM', freeNote: '100 metrics \u00d7 10 endpoints', pro: '10K DPM', proNote: 'then $1.49/1K DPM', enterprise: 'Custom', enterpriseNote: 'Volume discounts' }, + { name: 'Retention', free: '1 day', pro: '7 days', enterprise: 'Custom' }, + { name: 'Concurrent Endpoints', free: '10', pro: '100', proNote: 'then $0.50/100 endpoints', enterprise: 'Custom' }, + ], + }, + { + name: 'Hosting', + features: [ + { name: 'Relays', free: 'Public', freeNote: 'Multi-tenant', pro: 'Cloud', proNote: '$199/relay', enterprise: 'Custom' }, + { name: 'Connections per Relay', free: 'Variable', freeNote: 'Rate-limited', pro: '60k', enterprise: 'Custom' }, + { name: 'Multi-region', free: null, pro: true, enterprise: 'Custom' }, + { name: 'Multi-cloud', free: null, pro: null, enterprise: true }, + { name: 'Dedicated DNS', free: null, pro: null, enterprise: true }, + ], + }, + { + name: 'Support', + features: [ + { name: 'Community (Github & Discord)', free: true, pro: true, enterprise: true }, + { name: 'SLAs', free: null, pro: null, enterprise: 'Custom' }, + { name: 'Dedicated Support Engineer', free: null, pro: null, enterprise: true }, + ], + }, +] + +function FeatureCell({ value, note }) { + if (value === true) { + return ( + + + + ) + } + if (value === null || value === undefined) { + return ( + + + + ) + } + return ( + +
{value}
+ {note &&

{note}

} + + ) +} + +export default function PricingPage() { + return ( +
+ + +
+ {/* Hero */} +
+
+

Plans and Pricing

+

+ Go to production with confidence. +

+

+ Customized hosting and monitoring for iroh apps, with people that support you. +

+
+
+ + {/* Plan Cards */} +
+
+
+ {plans.map((plan) => ( +
+ {plan.popular && ( + + Most popular + + )} +

{plan.name}

+

+ {plan.description} +

+

+ {plan.price} + {plan.period && ( + + {plan.period} + + )} +

+
+
+
    + {plan.features.map((feature) => ( +
  • + + {feature} +
  • + ))} +
+
+ + {plan.buttonLabel} + +
+ ))} +
+
+
+ + {/* Feature Comparison Grid */} +
+
+

Compare plans

+
+ + + + + + + + + + {featureSections.map((section) => { + const Icon = sectionIcons[section.name] + return ( + + + + + {section.features.map((feature) => ( + + + + + + + ))} + + ) + })} + +
+ FreeProEnterprise
+ + {Icon && } + {section.name} + +
+ {feature.name} +
+
+
+
+ + {/* Calculator */} +
+
+ +
+
+ + {/* CTA */} +
+
+

+ Need hosting, support or development?{' '} + + Book a meeting + +

+
+
+
+ + +
+ ) +} + diff --git a/src/components/Header.jsx b/src/components/Header.jsx index a273f2f7..973fb623 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -30,7 +30,7 @@ export const navItems = [ ]}, {content: 'Docs', href: 'https://docs.iroh.computer/'}, {content: 'Blog', href: '/blog'}, - {content: 'Roadmap', href: '/roadmap'}, + {content: 'Pricing', href: '/pricing'}, ]; export function TopLevelNavItem({href, children}) { diff --git a/src/components/PricingCalculator.jsx b/src/components/PricingCalculator.jsx new file mode 100644 index 00000000..e3163021 --- /dev/null +++ b/src/components/PricingCalculator.jsx @@ -0,0 +1,183 @@ +'use client' + +import { useState } from 'react' + +const PRO_BASE = 19 +const INCLUDED_ENDPOINTS = 100 +const ENDPOINT_RATE = 0.50 +const METRICS_RATE = 1.49 +const RELAY_RATE = 199 + +const relayOptions = [0, 1, 2, 3, 4, 5] +const peakConnectionOptions = [100, 200, 300, 500, 1000, 2000, 5000, 10000] +const avgConnectionOptions = [1, 10, 50, 100, 250, 500, 1000, 2000, 5000] +const metricsPerNodeOptions = [87, 90, 100, 110] + +const frequencyOptions = [ + { label: 'Every minute', value: '1', factor: 1 }, + { label: 'Every 5 minutes', value: '0.2', factor: 0.2 }, + { label: 'Every hour', value: '0.0167', factor: 1 / 60 }, + { label: 'Every day', value: '0.0007', factor: 1 / 1440 }, +] + +function formatPrice(n) { + return `$${n.toFixed(2)}` +} + +function formatNumber(n) { + return n.toLocaleString() +} + +function SelectInput({ label, description, value, onChange, options, formatOption }) { + return ( +
+ +

{description}

+ +
+ ) +} + +export function PricingCalculator() { + const [relays, setRelays] = useState(1) + const [peakConnections, setPeakConnections] = useState(500) + const [avgConnections, setAvgConnections] = useState(100) + const [metricsPerNode, setMetricsPerNode] = useState(87) + const [frequency, setFrequency] = useState('1') + + const freq = frequencyOptions.find((f) => f.value === frequency)?.factor ?? 1 + const dpm = avgConnections * metricsPerNode * freq + + const extraConnections = Math.max(0, peakConnections - INCLUDED_ENDPOINTS) + const connectionsCost = (extraConnections / 100) * ENDPOINT_RATE + const metricsCost = (dpm / 1000) * METRICS_RATE + const relayCost = relays * RELAY_RATE + const total = PRO_BASE + connectionsCost + metricsCost + relayCost + + return ( +
+

Estimate your monthly cost

+

+ Adjust the values to see what Pro would cost for your workload. +

+
+
+ {/* Inputs */} +
+ + + + +
+ +

+ How often each endpoint pushes metrics +

+ +
+

+ Calculated DPM:{' '} + + {formatNumber(Math.round(dpm))} + +

+
+ + {/* Cost breakdown */} +
+

Cost breakdown

+
+
+ Pro plan base + {formatPrice(PRO_BASE)}/mo +
+ +
+
+ Relays + {formatPrice(relayCost)}/mo +
+

+ {relays} × ${RELAY_RATE}/each +

+
+ +
+
+ Connections + {formatPrice(connectionsCost)}/mo +
+

+ {extraConnections === 0 + ? 'Included in base plan' + : `${formatNumber(extraConnections)} extra \u00d7 $${ENDPOINT_RATE}/100`} +

+
+ +
+
+ Metrics DPM + {formatPrice(metricsCost)}/mo +
+

+ {formatNumber(Math.round(dpm))} DPM × ${METRICS_RATE}/1K +

+
+ +
+
+ Estimated total + {formatPrice(total)}/mo +
+
+
+
+
+
+
+ ) +}