Lumen
UI Components

Pricing Table

Lumen provides UI components so you can speed up your SaaS app development.

Pricing Table

We recommend using our shadcn component so you can easily customize the styling manually or by prompting cursor:

npx shadcn@latest add https://getlumen.dev/pricing-table.json

Frontend Integration

Import the PricingTable component and add it to your NextJS pricing page:

import { PricingTable } from "@/components/ui/pricing-table";
// OR if you don't want to use shadcn
// import { PricingTable } from '@getlumen/react'

export default function PricingPage() {
  return (
    <PricingTable
      lumenPublishableKey={process.env.NEXT_PUBLIC_LUMEN_PUBLISHABLE_KEY}
      userId={session?.user?.id}
      loginRedirectUrl="/login"
      paymentProvider="stripe" // 'stripe' (default) or 'dodo'
    />
  );
}

Required Props

  • lumenPublishableKey: Your Lumen publishable key from the environment variables
  • userId: The ID of the currently logged-in user
  • loginRedirectUrl: Clicking subscribe will redirect to this url if userId is null or undefined

Optional Props

  • paymentProvider: 'stripe' | 'dodo' (default: 'stripe').
  • environment: 'test' | 'live' (Dodo only, default: 'live').
  • redirectUrl: Where to return after checkout.
  • lumenHandlerUrl: URL of your backend endpoint to fetch a user's current subscription (see below). Defaults to /api/lumen in the same origin.

Showing Current Subscription Status in the Pricing Table (Optional)

If a logged-in user opens the Pricing Table, ideally, we would like to show them their current subscription status. To do this, add the lumenHandlerUrl prop. This requires setting up a backend endpoint that securely fetches subscription data.

Create Backend API Endpoint

On NextJS, you can create api/lumen/[...all]/route.ts for App Router (or any other backend framework - just make the endpoint and pass it in lumenHandlerUrl):

// api/lumen/[...all]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { lumenNextHandler } from "@getlumen/server";
import { auth } from "@clerk/nextjs/server";

const handler = async (request: NextRequest) => {
  const { userId } = await auth();

  if (!userId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.json(
    await lumenNextHandler({
      request,
      userId,
    })
  );
};

export {
  handler as GET,
  handler as POST,
  handler as PUT,
  handler as DELETE,
  handler as PATCH,
  handler as OPTIONS,
  handler as HEAD,
};
// api/lumen/[...all]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/server";
import { lumenNextHandler } from "@getlumen/server";

const handler = async (request: NextRequest) => {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.json(
    await lumenNextHandler({
      request,
      userId: user.id,
    })
  );
};

export {
  handler as GET,
  handler as POST,
  handler as PUT,
  handler as DELETE,
  handler as PATCH,
  handler as OPTIONS,
  handler as HEAD,
};
// api/lumen/[...all]/route.ts
import { auth } from "@/lib/auth";

export async function GET(request: Request) {
  const session = await auth.api.getSession({ headers: request.headers });
  if (!session?.user) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  const { searchParams } = new URL(request.url);
  const externalCustomerId = searchParams.get("externalCustomerId");

  const response = await fetch(
    `https://api.getlumen.dev/v1/customers/subscription-status?externalCustomerId=${encodeURIComponent(
      externalCustomerId
    )}`,
    {
      headers: {
        Authorization: `Bearer ${process.env.LUMEN_SECRET_KEY}`,
      },
    }
  );

  return Response.json(await response.json());
}

import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { lumenNextHandler } from "@getlumen/server";

const handler = async (request: NextRequest) => {
  const session = await auth.api.getSession({ headers: request.headers });
  if (!session?.user) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.json(
    await lumenNextHandler({
      request,
      userId: session.user.id,
    })
  );
};

export {
  handler as GET,
  handler as POST,
  handler as PUT,
  handler as DELETE,
  handler as PATCH,
  handler as OPTIONS,
  handler as HEAD,
};

Make sure your LUMEN_API_KEY and NEXT_PUBLIC_LUMEN_PUBLISHABLE_KEY are set in your env vars.

LUMEN_API_KEY=sk_live_...
NEXT_PUBLIC_LUMEN_PUBLISHABLE_KEY=pk_live...
<PricingTable userId={session?.user?.id} loginRedirectUrl="/login" />