web: New Pricing Page (#9821)

* Removes Pro, restructures pricing page

* Solves provider/credits

* Update apps/web-roo-code/src/app/pricing/page.tsx

Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>

* Updates agent landing pages to not mention a trial that doesn't exist

* Updates agent-specific landing pages to reflect new home and trial

* Indicate the agent landing page the user came from

* Clean up the carousel

---------

Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>
This commit is contained in:
Bruno Bergher 2025-12-04 15:40:07 +00:00 committed by GitHub
parent 0eddc97d13
commit 0ed5f3ccd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 128 additions and 295 deletions

View file

@ -33,8 +33,9 @@ export const content: AgentPageContent = {
],
},
cta: {
buttonText: "Start 14-day Free Trial",
disclaimer: "(cancel anytime)",
buttonText: "Try now for free",
disclaimer: "",
tracking: "&agent=pr-fixer",
},
},
howItWorks: {
@ -89,6 +90,6 @@ export const content: AgentPageContent = {
cta: {
heading: "Ship fixes, not follow-ups.",
description: "Let Roo Code's PR Fixer turn your review feedback into clean, ready-to-merge commits.",
buttonText: "Start 14-day Free Trial",
buttonText: "Try now for free",
},
}

View file

@ -1,17 +1,16 @@
import { Users, Building2, ArrowRight, Star, LucideIcon, Check, Cloud, PlugZap } from "lucide-react"
import { Users, ArrowRight, LucideIcon, Check, SquareTerminal, CornerRightDown, Cloud } from "lucide-react"
import type { Metadata } from "next"
import Link from "next/link"
import { Button } from "@/components/ui"
import { AnimatedBackground } from "@/components/homepage"
import { ContactForm } from "@/components/enterprise/contact-form"
import { SEO } from "@/lib/seo"
import { ogImageUrl } from "@/lib/og"
import { EXTERNAL_LINKS } from "@/lib/constants"
const TITLE = "Roo Code Cloud Pricing"
const TITLE = "Roo Code Pricing"
const DESCRIPTION =
"Simple, transparent pricing for Roo Code Cloud. The VS Code extension is free forever. Choose the cloud plan that fits your needs."
"Simple, transparent pricing for all Roo Code products. The VS Code extension is free forever. Choose the cloud plan that fits your needs."
const OG_DESCRIPTION = ""
const PATH = "/pricing"
@ -61,6 +60,7 @@ interface PricingTier {
name: string
icon: LucideIcon
price: string
priceSuffix: string
period?: string
creditPrice?: string
trial?: string
@ -70,60 +70,57 @@ interface PricingTier {
cta: {
text: string
href?: string
isContactForm?: boolean
}
}
const pricingTiers: PricingTier[] = [
{
name: "Cloud Free",
icon: Cloud,
price: "$0",
description: "For folks just getting started",
features: [
"Token usage analytics",
"Access to the Roo Code Cloud Provider, including early access to free stealth models",
"Follow your tasks from anywhere",
"Share tasks with friends and co-workers",
"Community support",
],
name: "VS Code Extension",
icon: SquareTerminal,
price: "Free",
priceSuffix: "inference",
description: "The best local coding agent",
features: ["Unlimited local use", "Bring your own model", "Powerful, extensible modes", "Community support"],
cta: {
text: "Get started",
href: EXTERNAL_LINKS.CLOUD_APP_SIGNUP,
text: "Install Now",
href: EXTERNAL_LINKS.MARKETPLACE,
},
},
{
name: "Cloud Pro",
icon: Star,
price: "$20",
name: "Cloud Free",
icon: Cloud,
price: "$0",
period: "/mo",
trial: "Free for 14 days, then",
priceSuffix: "credits",
creditPrice: `$${PRICE_CREDITS}`,
description: "For pro Roo coders",
featuresIntro: "Everything in Free +",
description: "For AI-forward engineers",
featuresIntro: "Go beyond the extension with",
features: [
"Cloud Agents: Coder, Explainer, Planner, Reviewer, Fixer and more",
"Start tasks from Slack",
"Roomote Control: Start, stop and control extension tasks from anywhere",
"Paid support",
"Access to Cloud Agents: fully autonomous development you can call from Slack, Github and the web",
"Access to the Roo Code Cloud Provider",
"Follow your tasks from anywhere",
"Share tasks with friends and co-workers",
"Token usage analytics",
"Professional support",
],
cta: {
text: "Get started",
href: EXTERNAL_LINKS.CLOUD_APP_SIGNUP + "?redirect_url=/billing",
text: "Sign up",
href: EXTERNAL_LINKS.CLOUD_APP_SIGNUP,
},
},
{
name: "Cloud Team",
icon: Users,
price: "$99",
priceSuffix: "credits",
period: "/mo",
creditPrice: `$${PRICE_CREDITS}`,
trial: "Free for 14 days, then",
description: "For AI-forward teams",
featuresIntro: "Everything in Pro +",
featuresIntro: "Everything in Free +",
features: ["Unlimited users (no per-seat cost)", "Shared configuration & policies", "Centralized billing"],
cta: {
text: "Get started",
text: "Sign up",
href: EXTERNAL_LINKS.CLOUD_APP_SIGNUP + "?redirect_url=/billing",
},
},
@ -135,70 +132,43 @@ export default function PricingPage() {
<AnimatedBackground />
{/* Hero Section */}
<section className="relative overflow-hidden pt-16 pb-12">
<section className="relative overflow-hidden pt-12 pb-10">
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h1 className="text-5xl font-bold tracking-tight">Roo Code Cloud Pricing</h1>
<p className="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground">
Simple, transparent pricing that scales with your needs.
<br />
No inference markups. Free 14-day trials to kick the tires.
<h1 className="text-5xl font-bold tracking-tight">Roo Code Pricing</h1>
<p className="mt-4 text-lg text-muted-foreground">
For all of our products: the Roo Code VS Code Extension, Roo Code Cloud and the Roo Code
Cloud inference Provider.
</p>
</div>
</div>
</section>
<div className="mx-6 md:mx-auto max-w-6xl">
<div className="rounded-xl p-4 mb-8 text-center bg-gradient-to-r from-blue-500/10 via-cyan-500/10 to-purple-500/10 border border-blue-500/20 dark:border-white/20 ">
<p className="text-center">
<strong className="font-semibold">The Roo Code extension is totally free! </strong>
But Cloud takes you so much further.
</p>
</div>
</div>
<div className="mx-6 md:mx-auto max-w-6xl p-7 mb-4 relative flex flex-col justify-start bg-background border rounded-2xl transition-all shadow-none hover:shadow-lg">
<h3 className="text-xl font-semibold flex items-center gap-2 justify-between">
Roo Code Provider
<PlugZap className="size-6" />
</h3>
<div className="text-sm text-muted-foreground space-y-1 mt-2">
<p className="">
On any plan, you can bring your own provider key or use the built-in Roo Code Cloud provider.
</p>
<p className="text-sm text-muted-foreground">
We offer a select mix of tested state of the art closed and open weight LLMs for you to choose,
with no markup.
<Link href="/provider/pricing" className="underline hover:no-underline ml-1">
See detailed pricing
</Link>
</p>
</div>
</div>
{/* Pricing Tiers */}
<section className="">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto grid max-w-6xl gap-4 lg:grid-cols-3">
<div className="mx-auto grid max-w-6xl gap-4 md:grid-cols-3 md:px-4">
{pricingTiers.map((tier) => {
const Icon = tier.icon
return (
<div
key={tier.name}
className="relative p-6 flex flex-col justify-start bg-background border rounded-2xl transition-all hover:shadow-lg">
className="relative group p-6 flex flex-col justify-start bg-background rounded-2xl outline outline-2 outline-border/50 hover:outline-8 transition-all shadow-xl hover:shadow-2xl hover:outline-6">
<div className="mb-6">
<div className="flex items-center justify-between">
<h3 className="text-2xl font-bold tracking-tight">{tier.name}</h3>
<Icon className="size-6" />
</div>
<p className="text-sm text-muted-foreground">{tier.description}</p>
<p className="text-sm font-medium">{tier.description}</p>
</div>
<div className="absolute -right-2 -top-4 rounded-full bg-card shadow-md p-4 outline outline-2 outline-border/50 group-hover:scale-105 group-hover:outline-8 transition-all">
<Icon className="size-6" strokeWidth={1.5} />
</div>
<div className="grow mb-8">
<p className="text-sm text-muted-foreground font-light mb-2">
{tier.featuresIntro}&nbsp;
</p>
<ul className="space-y-3 my-0 h-[168px]">
<ul className="space-y-3 my-0 md:h-[192px]">
{tier.features.map((feature) => (
<li key={feature} className="flex items-start gap-2">
<Check className="mt-0.5 h-4 w-4 text-muted-foreground shrink-0" />
@ -210,58 +180,65 @@ export default function PricingPage() {
<p className="text-base font-light">{tier.trial}</p>
<p className="text-xl my-1 tracking-tight font-light">
<p className="text-xl mb-1 tracking-tight font-light">
<strong className="font-bold">{tier.price}</strong>
{tier.period} + prepaid credits
{tier.period} + {tier.priceSuffix}
<CornerRightDown className="inline size-4 ml-1 relative top-0.5" />
</p>
<p className="text-sm text-muted-foreground mb-3">
<p className="text-sm text-muted-foreground mb-5">
{tier.creditPrice && (
<>
Cloud Agents: {tier.creditPrice}/hour if used
Cloud Agents: {tier.creditPrice}/hour in credits
<br />
</>
)}
Inference:{" "}
<Link href="/provider/pricing" className="underline hover:no-underline">
Roo Provider pricing
Roo Provider
</Link>{" "}
or{" "}
<abbr title="Bring Your Own Key" className="cursor-help">
BYOK
credits or{" "}
<abbr title="Bring Your Own Model" className="cursor-help">
BYOM
</abbr>
</p>
{tier.cta.isContactForm ? (
<ContactForm
formType="demo"
buttonText={tier.cta.text}
buttonClassName="w-full transition-all duration-300"
/>
) : (
<Button size="lg" className="w-full transition-all duration-300" asChild>
<Link href={tier.cta.href!} className="flex items-center justify-center">
{tier.cta.text}
</Link>
</Button>
)}
<Button size="lg" className="w-full transition-all duration-300" asChild>
<Link href={tier.cta.href!} className="flex items-center justify-center">
{tier.cta.text}
<ArrowRight />
</Link>
</Button>
{/* <div className="bg-foreground/20 h-8 absolute -bottom-8 left-1/2 w-[1px]" /> */}
<div className="h-[28px] absolute bottom-[-31px] left-1/2 w-[4px] transition-colors bg-gradient-to-b from-transparent to-violet-700/20 group-hover:from-violet-500/50 group-hover:to-violet-500/20" />
</div>
)
})}
</div>
</div>
<div className="mx-auto grid max-w-6xl gap-4 mt-4 relative">
<p className="bg-background border rounded-2xl p-6 text-center text-sm text-muted-foreground">
<Building2 className="inline size-4 mr-2 mb-0.5" />
Need SAML, advanced security, custom integrations or terms? Enterprise is for you.
<Link
href="/enterprise#contact"
className="font-medium ml-1 text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300">
Talk to Sales
</Link>
.
</p>
<div className="max-w-6xl mx-auto mt-8 p-7 flex flex-col md:flex-row gap-8 md:gap-4 bg-violet-200/20 outline-violet-700/20 outline outline-1 rounded-2xl transition-all shadow-none">
<div className="md:border-r md:pr-4">
<h3 className="text-lg font-medium mb-1">Roo Code Provider</h3>
<div className="text-sm text-muted-foreground">
<p className="">
On any plan, you can use your own LLM provider API key or use the built-in Roo Code
Cloud provider curated models to work with Roo with no markup, including the
latest Gemini, GPT and Claude. Paid with credits.
<Link href="/provider/pricing" className="underline hover:no-underline ml-1">
See per model pricing.
</Link>
</p>
</div>
</div>
<div className="">
<h3 className="text-lg font-medium mb-1">Credits</h3>
<p className="text-sm text-muted-foreground">
Credits are pre-paid, in dollars, and are deducted with usage for inference and Cloud
Agent runs. You&apos;re always in control of your spend, no surprises.
</p>
</div>
</div>
</div>
</section>
@ -337,6 +314,19 @@ export default function PricingPage() {
reflected in your next billing cycle.
</p>
</div>
<div className="rounded-xl border border-border bg-card p-6 md:col-span-2">
<h3 className="font-semibold">
What if I have enterprise-level needs like SAML/SCIM, large-scale deployments, specific
integrations and custom terms?
</h3>
<p className="mt-2 text-sm text-muted-foreground">
We have an Enterprise plan which can be a fit. Please{" "}
<Link href="/enterprise#contact" className="underline hover:no-underline">
reach out to our sales team
</Link>{" "}
to discuss it.
</p>
</div>
</div>
<div className="mt-12 text-center">

View file

@ -32,8 +32,9 @@ export const content: AgentPageContent = {
],
},
cta: {
buttonText: "Start 14-day Free Trial",
disclaimer: "(cancel anytime)",
buttonText: "Try now for free",
disclaimer: "",
tracking: "&agent=reviewer",
},
},
howItWorks: {
@ -87,6 +88,6 @@ export const content: AgentPageContent = {
cta: {
heading: "Ready for better code reviews?",
description: "Start finding the issues that matter with AI-powered reviews built for depth, not cost-cutting.",
buttonText: "Start 14-day Free Trial",
buttonText: "Try now for free",
},
}

View file

@ -32,8 +32,9 @@ export const content: AgentPageContent = {
],
},
cta: {
buttonText: "Start 14-day Free Trial",
disclaimer: "(cancel anytime)",
buttonText: "Try now for free",
disclaimer: "",
tracking: "&agent=reviewer",
},
},
howItWorks: {
@ -87,6 +88,6 @@ export const content: AgentPageContent = {
cta: {
heading: "Ready for better code reviews?",
description: "Start finding the issues that matter with AI-powered reviews built for depth, not cost-cutting.",
buttonText: "Start 14-day Free Trial",
buttonText: "Try now for free",
},
}

View file

@ -16,8 +16,7 @@ import Image from "next/image"
import Link from "next/link"
import { Button } from "@/components/ui"
import { AnimatedBackground } from "@/components/homepage"
import { AgentCarousel } from "@/components/reviewer/agent-carousel"
import { AnimatedBackground, UseExamplesSection } from "@/components/homepage"
import { EXTERNAL_LINKS } from "@/lib/constants"
import { type AgentPageContent, type IconName } from "./agent-page-content"
@ -93,7 +92,7 @@ export function AgentLandingContent({ content }: { content: AgentPageContent })
className="w-full sm:w-auto backdrop-blur-sm border hover:shadow-[0_0_20px_rgba(59,130,246,0.5)] transition-all duration-300"
asChild>
<a
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP_PRO}
href={`${EXTERNAL_LINKS.CLOUD_APP_SIGNUP_PRO}${content.hero.cta.tracking}`}
target="_blank"
rel="noopener noreferrer"
className="flex w-full items-center justify-center">
@ -127,6 +126,9 @@ export function AgentLandingContent({ content }: { content: AgentPageContent })
{/* How It Works Section */}
<section className="relative overflow-hidden border-t border-border py-32">
<div className="absolute inset-y-0 left-1/2 h-full w-full max-w-[1200px] -translate-x-1/2 z-1">
<div className="absolute left-1/2 top-1/2 h-[400px] w-full -translate-x-1/2 -translate-y-1/2 rounded-full bg-violet-500/10 dark:bg-violet-700/20 blur-[140px]" />
</div>
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto mb-12 md:mb-24 max-w-5xl text-center">
<div>
@ -161,6 +163,9 @@ export function AgentLandingContent({ content }: { content: AgentPageContent })
{/* Why Better Section */}
<section className="relative overflow-hidden border-t border-border py-32">
<div className="absolute inset-y-0 left-1/2 h-full w-full max-w-[1200px] -translate-x-1/2 z-1">
<div className="absolute left-1/2 top-1/2 h-[400px] w-full -translate-x-1/2 -translate-y-1/2 rounded-full bg-blue-500/10 dark:bg-blue-700/20 blur-[140px]" />
</div>
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto mb-12 md:mb-24 max-w-5xl text-center">
<div>
@ -197,8 +202,7 @@ export function AgentLandingContent({ content }: { content: AgentPageContent })
</div>
</section>
{/* Agent Carousel */}
<AgentCarousel currentAgent={content.agentName} />
<UseExamplesSection agentTitle={true} />
{/* CTA Section */}
<section className="py-20">
@ -214,11 +218,11 @@ export function AgentLandingContent({ content }: { content: AgentPageContent })
className="bg-black text-white hover:bg-gray-800 hover:shadow-lg hover:shadow-black/20 dark:bg-white dark:text-black dark:hover:bg-gray-200 dark:hover:shadow-white/20 transition-all duration-300"
asChild>
<a
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP_PRO}
href={`${EXTERNAL_LINKS.CLOUD_APP_SIGNUP_PRO}${content.hero.cta.tracking}`}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center">
{content.cta.buttonText}
{content.hero.cta.buttonText}
<ArrowRight className="ml-2 h-4 w-4" />
</a>
</Button>

View file

@ -21,7 +21,6 @@ export type IconName =
* serialization from Server Components to Client Components.
*/
export interface AgentPageContent {
/** The agent name used for the carousel display */
agentName: string
hero: {
/** Optional icon name to display in the hero section */
@ -45,6 +44,7 @@ export interface AgentPageContent {
cta: {
buttonText: string
disclaimer: string
tracking: string
}
}
howItWorks: {

View file

@ -372,7 +372,7 @@ function DesktopUseCaseCard({ item }: { item: PositionedUseCase }) {
)
}
export function UseExamplesSection() {
export function UseExamplesSection({ agentTitle = false }: { agentTitle?: boolean }) {
const positionedItems = useMemo(() => distributeItems(USE_CASES), [])
const [showAllMobile, setShowAllMobile] = useState(false)
@ -384,7 +384,15 @@ export function UseExamplesSection() {
<div className="container px-4 mx-auto sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-4xl font-bold tracking-tight mb-4">
The AI team to help your <em>entire</em> human team
{agentTitle ? (
<>
Part of the AI team to help your <em>entire</em> human team
</>
) : (
<>
The AI team to help your <em>entire</em> human team
</>
)}
</h2>
<p className="text-xl font-light text-muted-foreground max-w-2xl mx-auto">
Developers, PMs, Designers, Customer Success: everyone moves faster and more independently with

View file

@ -1,172 +0,0 @@
"use client"
import { useEffect } from "react"
import { motion } from "framer-motion"
import useEmblaCarousel from "embla-carousel-react"
import AutoPlay from "embla-carousel-autoplay"
import {
Bug,
FileText,
Gauge,
GitPullRequest,
Languages,
Microscope,
PocketKnife,
TestTube,
Wrench,
type LucideIcon,
} from "lucide-react"
// AI Agent types for the carousel
interface AIAgent {
icon: LucideIcon
name: string
page?: string
}
const aiAgents: AIAgent[] = [
{ icon: GitPullRequest, name: "PR Reviewer", page: "/reviewer" },
{ icon: Wrench, name: "PR Fixer", page: "/pr-fixer" },
{ icon: PocketKnife, name: "Generalist" },
{ icon: Bug, name: "Bug Fixer" },
{ icon: TestTube, name: "Test Engineer" },
{ icon: Microscope, name: "Security Auditor" },
{ icon: Gauge, name: "Performance Optimizer" },
{ icon: FileText, name: "Documentation Writer" },
{ icon: Languages, name: "String Translator" },
]
export function AgentCarousel({ currentAgent = "" }: { currentAgent?: string } = {}) {
const [emblaRef, emblaApi] = useEmblaCarousel(
{
loop: true,
align: "start",
watchDrag: true,
dragFree: false,
containScroll: false,
duration: 10000,
},
[
AutoPlay({
playOnInit: true,
delay: 0,
stopOnInteraction: false,
stopOnMouseEnter: false,
stopOnFocusIn: false,
}),
],
)
// Continuous scrolling effect
useEffect(() => {
if (!emblaApi) return
const autoPlay = emblaApi?.plugins()?.autoPlay as
| {
play?: () => void
}
| undefined
if (autoPlay?.play) {
autoPlay.play()
}
// Set up continuous scrolling
const interval = setInterval(() => {
if (emblaApi) {
emblaApi.scrollNext()
}
}, 30) // Smooth continuous scroll
return () => clearInterval(interval)
}, [emblaApi])
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
duration: 0.6,
ease: [0.21, 0.45, 0.27, 0.9],
},
},
}
// Duplicate the agents array for seamless infinite scroll
const displayAgents = [...aiAgents, ...aiAgents]
return (
<section className="relative overflow-hidden border-t border-border py-32">
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto mb-12 max-w-4xl text-center">
<div>
<h2 className="text-4xl font-bold tracking-tight sm:text-5xl">
The first members of a whole new team
</h2>
<p className="mt-6 text-lg text-muted-foreground">
Architecture, coding, reviewing, testing, debugging, documenting, designing almost
everything we do today is mostly through our agents. Now we&apos;re bringing them to you.
</p>
<p className="mt-2 text-lg text-muted-foreground">
Roo&apos;s {currentAgent} isn&apos;t yet another single-purpose tool to add to your already
complicated stack. It&apos;s the first member of your AI-powered development team. More
agents are shipping soon.
</p>
</div>
</div>
<div className="relative mx-auto md:max-w-[1200px]">
<motion.div
className="relative -mx-4 md:mx-auto max-w-[1400px]"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}>
{/* Gradient Overlays */}
<div className="absolute inset-y-0 left-0 z-10 w-[10%] bg-gradient-to-r from-background to-transparent pointer-events-none md:w-[15%]" />
<div className="absolute inset-y-0 right-0 z-10 w-[10%] bg-gradient-to-l from-background to-transparent pointer-events-none md:w-[15%]" />
{/* Embla Carousel Container */}
<div className="overflow-hidden" ref={emblaRef}>
<div className="flex pb-4">
{displayAgents.map((agent, index) => {
const Icon = agent.icon
return (
<div
key={`${agent.name}-${index}`}
className="relative min-w-0 flex-[0_0_45%] px-2 md:flex-[0_0_30%] md:px-4 lg:flex-[0_0_15%]">
<div className="group relative py-6 cursor-default">
<div
className="relative flex flex-col items-center justify-center rounded-full w-[150px] h-[150px] border border-border bg-background p-6 transition-all duration-500 ease-out shadow-xl
hover:scale-110 hover:-translate-y-2
hover:shadow-[0_20px_50px_rgba(39,110,226,0.25)] dark:hover:shadow-[0_20px_50px_rgba(59,130,246,0.25)]">
<Icon
strokeWidth={1}
className="size-9 mb-2 text-foreground transition-colors duration-300"
/>
<h3 className="text-center leading-tight tracking-tight transition-colors duration-300 dark:text-foreground">
{agent.page ? (
<a
href={agent.page}
className="text-foreground/90 font-semibold">
{agent.name}
</a>
) : (
<span className="text-foreground/60 font-medium">
{agent.name}
</span>
)}
</h3>
</div>
</div>
</div>
)
})}
</div>
</div>
</motion.div>
</div>
</div>
</section>
)
}