Autumn handles your billing flows, so you don’t need to build and maintain user flows like:

  • Purchasing a product
  • Inputting quantities for prepaid products
  • Upgrading, downgrading, cancelling or renewing a product’s subscription
  • Paywalling features
  • Failed payments [coming soon]

Autumn offers the components in 2 forms: React components to drop in, or customizable shadcn/ui components that can be downloaded and edited.

Pricing Table

The pricing table component displays your available products, the features available in each and any prices. You can also pass in a productDetails prop to add information to the pricing card contents.

React

import { PricingTable } from "autumn-js/react";

shadcn/ui

npx shadcn@latest add https://ui.useautumn.com/pricing-table

This will download the pricing-table component in your /components directory, under a /autumn folder.

Pricing Table

Usage

import { PricingTable } from "@components/autumn/pricing-table";

export const PricingTableExample = () => (
  <div>
    <PricingTable productDetails={productDetails} />
  </div>
);

The component can take in productDetails, which can be used to pass in content that can override the default contents (which are generated from your Autumn products). The example below was used to render the screenshot above.

You can choose which products should be displayed, and what items each card should have. The items array can either take an object with primaryText and secondaryText properties, or a string with the featureId, which will just take the item from Autumn’s default contents.

const productDetails = [
  {
    id: "free",
    description: "A great free plan to get started with",
  },
  {
    id: "pro",
    description: "For all your extra messaging needs",
    recommendText: "Most Popular",
    price: {
      primaryText: "$10/month",
      secondaryText: "billed monthly",
    },
    items: [
      {
        featureId: "messages",
      },
      {
        primaryText: "Premium support",
        secondaryText: "Get help from our team",
      },
    ],
  },
  {
    id: "pro_annual",
  },
  {
    id: "premium",
    description: "For those of you who are really serious",
  },
  {
    id: "premium_annual",
  },
];

Product Change

The attach dialog will open when a customer wants to change their active product, such as when they upgrade or downgrade, to let them preview how much will be charged, and confirm the change.

React

import { AttachDialog } from "autumn-js/react";

shadcn/ui

npx shadcn@latest add https://ui.useautumn.com/attach-dialog
Product Change Dialog

This will download the attach-dialog component in your /components directory, under a /autumn folder.

Usage

Pass in the attach-dialog component to the attach() hook. This allows Autumn to automatically open the attach dialog when they want to change their active product. The dialog will open if:

  • The customer needs to input a quantity for their purchase (ie, if the product has prepaid prices)
  • If the customer’s payment method is already on file (ie, for upgrades, downgrades or add-ons).

When the customer confirms the purchase, Autumn will attach the product to the customer.

If there are no inputs required and it’s a new customer (without an existing payment method), the customer will be redirected straight to the Stripe checkout page.

import AttachDialog from "@/components/autumn/attach-dialog";
import { useCustomer } from "autumn-js/react";

const { attach } = useCustomer();

<Button
  onClick={async () =>
    await attach({
      productId: "pro",
      dialog: AttachDialog,
    })
  }
/>;

Scenarios

When you install attach-dialog, a @/lib/autumn/attach-content.tsx file is also installed. This file contains the dialog texts for each scenario (returned from the attach function), which you can customize how you want.

/lib/autumn/get-attach-content.tsx
import { CheckProductPreview } from "autumn-js";

export const getAttachContent = (preview: CheckProductPreview) => {
  const {
    scenario,
    product_name,
    recurring,
    current_product_name,
    next_cycle_at,
  } = preview;

  const nextCycleAtStr = next_cycle_at
    ? new Date(next_cycle_at).toLocaleDateString()
    : undefined;

  switch (scenario) {
    case "scheduled":
      return {
        title: <p>Scheduled product already exists</p>,
        message: <p>You already have this product scheduled to start soon.</p>,
      };

    case "active":
      return {
        title: <p>Product already active</p>,
        message: <p>You are already subscribed to this product.</p>,
      };

    case "new":
      if (recurring) {
        return {
          title: <p>Subscribe to {product_name}</p>,
          message: (
            <p>
              By clicking confirm, you will be subscribed to {product_name} and
              your card will be charged immediately.
            </p>
          ),
        };
      } else {
        return {
          title: <p>Purchase {product_name}</p>,
          message: (
            <p>
              By clicking confirm, you will purchase {product_name} and your
              card will be charged immedaitely.
            </p>
          ),
        };
      }

    case "renew":
      return {
        title: <p>Renew</p>,
        message: (
          <p>
            By clicking confirm, you will renew your subscription to{" "}
            {product_name}.
          </p>
        ),
      };

    case "upgrade":
      return {
        title: <p>Upgrade to {product_name}</p>,
        message: (
          <p>
            By clicking confirm, you will upgrade your subscription to{" "}
            {product_name} and your card will be charged immediately.
          </p>
        ),
      };

    case "downgrade":
      return {
        title: <p>Downgrade to {product_name}</p>,
        message: (
          <p>
            By clicking confirm, your current subscription to{" "}
            {current_product_name} will be cancelled and a new subscription to{" "}
            {product_name} will begin on {nextCycleAtStr}.
          </p>
        ),
      };

    case "cancel":
      return {
        title: <p>Cancel</p>,
        message: (
          <p>
            By clicking confirm, your subscription to {current_product_name}{" "}
            will end on {nextCycleAtStr}.
          </p>
        ),
      };

    default:
      return {
        title: <p>Change Subscription</p>,
        message: <p>You are about to change your subscription.</p>,
      };
  }
};

Paywall

A paywall that prompts users to upgrade to the next tier when they hit a usage limit, or don’t have access to a feature.

Install

npx shadcn@latest add https://ui.useautumn.com/check-dialog
Paywall Dialog

This will download the check-dialog component in your /components directory, under a /autumn folder.

Usage

Pass in the check-dialog component to the check function (exported from the useCustomer hook).

import CheckDialog from "@/components/autumn/check-dialog";
import { useCustomer } from "autumn-js/react";

const { check } = useCustomer();

const { data, error } = await check({
  featureId: "ai-messages",
  dialog: CheckDialog,
});

Now when you use the check() function, Autumn will automatically pass in the withPreview: true parameter. If data.allowed: false is returned for the feature, the paywall will open with the following details:

  • The feature they don’t have access to, or have run out of
  • The next product tier they should upgrade to, in order to access the feature (or an add-on if no additional tier exists)
  • A button that lets them purchase the next tier or add-on

Scenarios

The check({withPreview: true}) call can return a preview object that contains a scenario enum, which is used to control the dialog’s contents. It can be one of the following:

ScenarioDescription
usage_limitThe customer has hit a usage limit for the feature
feature_flagThe customer doesn’t have access to the feature

The preview object also contains a products array, which contains the products that the customer can purchase to access the feature.

When the check-dialog component is installed, a /lib/autumn/check-content.tsx file is also installed. This file contains the dialog texts for each scenario (depending on the available products), which you can customize how you want.

/lib/autumn/get-check-content.tsx
import { CheckFeaturePreview } from "autumn-js";

export const getPaywallDialogTexts = (preview: CheckFeaturePreview) => {
  const { scenario, products, feature_name } = preview;

  if (products.length == 0) {
    switch (scenario) {
      case "usage_limit":
        return {
          title: `Feature Unavailable`,
          message: `You have reached the usage limit for ${feature_name}. Please contact us to increase your limit.`,
        };
      default:
        return {
          title: "Feature Unavailable",
          message:
            "This feature is not available for your account. Please contact us to enable it.",
        };
    }
  }

  const nextProduct = products[0];

  const isAddOn = nextProduct && nextProduct.is_add_on;

  const title = nextProduct.free_trial
    ? `Start trial for ${nextProduct.name}`
    : nextProduct.is_add_on
    ? `Purchase ${nextProduct.name}`
    : `Upgrade to ${nextProduct.name}`;

  let message = "";
  if (isAddOn) {
    message = `Please purchase the ${nextProduct.name} add-on to continue using ${feature_name}.`;
  } else {
    message = `Please upgrade to the ${nextProduct.name} plan to continue using ${feature_name}.`;
  }

  switch (scenario) {
    case "usage_limit":
      return {
        title: title,
        message: `You have reached the usage limit for ${feature_name}. ${message}`,
      };
    case "feature_flag":
      return {
        title: title,
        message: `This feature is not available for your account. ${message}`,
      };
    default:
      return {
        title: "Feature Unavailable",
        message: "This feature is not available for your account.",
      };
  }
};