Building a Stripe App in 5 easy steps
During Sessions 2022 we announced Stripe Apps, the new way to extend Stripe.
If you haven’t already, you should watch the keynote and subsequent breakout session to understand why Stripe Apps was built, and what the future looks like for this new product.
In this post we’ll cover the very basics of getting started with building a Stripe App in just a few easy steps.
Pre-flight
You might already be familiar with the Stripe CLI. In case you aren’t, it’s a tool we built to help you integrate with Stripe as fast as possible. It has many features that can help you in building your integration, but we’re going to focus today on its use as the starting point for building your Stripe App.
As a prerequisite to this list, make sure you have the CLI installed and that you are using at least version 1.8.0 of the CLI.
1. Install the apps plugin
In your terminal, install the necessary apps plugin by running:
$ stripe plugin install apps
2. Create your app
Next, create your app with the command:
$ stripe apps create APP_NAME
Replacing APP_NAME
with whatever you want to call your app. In the screenshot below I used example-app
.
3. Provide your App ID and Display Name
The App ID is the unique ID associated with your app. These namespaces work similarly to package names in the NPM registry, in that they must be unique to prevent conflicts. This only matters if you’re planning on uploading your app later, so don’t worry too much if you are just testing things out for now.
Display name is the user facing name of your app that you’ll see in the Stripe dashboard. The CLI will try to guess a default value based on what you passed in.
If you’re having difficulty thinking of that perfect name (naming is hard!), don’t worry too much about it as both these values can be changed later.
Once naming is settled (or not, naming is still hard), the CLI will download and install all the necessary requirements for the app, create a new directory and initialise a new git repo.
4. Navigate to the new folder and run the app
Next, cd
into the new directory and run:
$ stripe apps start
Once you hit “enter” you’ll be prompted to pick a Stripe account to run your apps on. Once you’ve confirmed, you’ll be redirected to the dashboard where you’ll see your app appear in the side panel on the right.
The boilerplate “Hello World” app defaults to the stripe.dashboard.customer.detail
viewport, meaning that the app only does something when you navigate to a customer detail view. You could use this space to for instance display a list of “todo” items.
Many other viewports are supported.
5. See your changes reflected in the dashboard
Now that you have a basic app up and running, let’s make some changes to the code and see what happens. Open up /src/views/CustomerDetailView.tsx
in your favourite IDE take a peek.
If you already know React, then this should hopefully look pretty familiar. As you might have inferred from the file suffix, Stripe Apps are written in TypeScript and React.
💡 Curious about how we built Stripe Apps? Make sure you join the Stripe Apps Q&A on June 2nd 2022!
The code you see is the example “Hello World” app you should see in your browser.
The ContextView
component is the main viewpoint for your app and where your users will likely spend most of their time. It’s the area rendered to the right of the dashboard in its own side panel.
Let’s start fresh and remove everything inside the ContextView
and the StyledLink
component at the end. You should be left with something like this:
import {
Box,
ContextView,
Divider,
Icon,
Link,
} from "@stripe/ui-extension-sdk/ui";
import type { ExtensionContextValue } from "@stripe/ui-extension-sdk/context";
/**
* This is a view that is rendered in the Stripe dashboard's customer detail page.
* In stripe-app.json, this view is configured with stripe.dashboard.customer.detail viewport.
* You can add a new view by running "stripe apps add view" from the CLI.
*/
const CustomerDetailView = ({ userContext, environment }: ExtensionContextValue) => {
return (
<ContextView title="Your App">
</ContextView>
);
};
export default CustomerDetailView;
Save the file and head back to your browser. You should see that the app has automatically updated and is now a rather boring white screen with just the “Your App” title.
Stripe Apps uses Hot Module Replacement, which means updates to your app’s code can be seen in the dashboard immediately without requiring a page refresh.
You might notice, however, that your app isn’t running on a localhost server, but right in the Stripe production Dashboard. This means that you have direct access to your test mode Stripe data whilst developing your app.
Whilst playing around with the code, you might notice that if you try to render a standard HTML node like a div
, you get the error:
Unsupported component: div
Only components imported from the ui-extension-sdk
can be used in Stripe Apps. This restriction serves two purposes:
- Security: This keeps both you and your users safe by limiting what can and can’t be done in the app sandbox. For example, an app can’t access sensitive data about a user’s Stripe account and a user can’t access an app’s secrets.
- Design cohesion: It ensures your app has the look and feel of the Stripe Dashboard.
Let’s improve on our boring screen and add a few lines of code that demonstrate using the components. We’re going to build a very simple app that shows how many times we’ve clicked a button, just to demonstrate how our components work together with familiar concepts like React hooks.
import {
Box,
Button,
ContextView,
Icon,
Inline,
} from "@stripe/ui-extension-sdk/ui";
import type { ExtensionContextValue } from "@stripe/ui-extension-sdk/context";
import { useState } from "react";
/**
* This is a view that is rendered in the Stripe dashboard's customer detail page.
* In stripe-app.json, this view is configured with stripe.dashboard.customer.detail viewport.
* You can add a new view by running "stripe apps add view" from the CLI.
*/
const CustomerDetailView = ({ userContext, environment }: ExtensionContextValue) => {
const [timesClicked, setTimesClicked] = useState<number>(0);
const incrementClick = () => {
setTimesClicked(timesClicked + 1);
};
const clear = () => {
setTimesClicked(0);
};
return (
<ContextView title="Your App">
<Box css={{
layout: 'row',
gap: 'small',
alignX: 'center',
}}>
<Button type="primary" onPress={() => incrementClick()}>
<Icon name="add"/>
<Inline css={{ marginLeft: 'small' }}>Click me!</Inline>
</Button>
<Button onPress={() => clear()}>
<Icon name="trash" />
<Inline css={{ marginLeft: 'small' }}>Clear</Inline>
</Button>
</Box>
<Box css={{
padding: 'large',
alignX: 'center',
layout: 'row'
}}>
{`I've been clicked ${timesClicked} times!`}
</Box>
</ContextView>
);
};
export default CustomerDetailView;
Here we’ve added some new components, Box
, Button
, Icon
and Inline
.
Box
and Inline
can be considered similar to div
and span
in regular HTML in that that they are generic containers. They differ from other components in that they have access to the css
prop, where you can pass in style options.
Button
and Icon
are fairly straightforward, the latter allows you access to a library of pre-designed icons.
Now save the file, switch to your browser and let’s see our app in action:
There we have a very simple app, built with the UI Extension SDK and React hooks!
In the next post, we’ll go into more complicated use cases like using the Stripe API from our app and incorporating a back end.
In the meanwhile, let us know what you’re working on or what you plan to build by staying in touch in these ways 👇
📣 Follow @StripeDev and our team on Twitter
📺 Subscribe to our Youtube channel
💬 Join the official Discord server
📧 Sign up for the Dev Digest
About the author
Paul Asjes is a Developer Advocate at Stripe where he writes, codes and hosts a monthly Q&A series talking to developers. Outside of work he enjoys brewing beer, making biltong and losing to his son in Mario Kart.