Building a Stripe App in 5 easy steps
Tags: javascript, stripe, tutorial, stripeapps
Published: 2022-05-24 18:46:42

During [Sessions 2022](https://sessions.stripe.com/) we announced [Stripe Apps](https://stripe.com/apps), the new way to extend Stripe.

If you haven’t already, you should watch the [keynote](https://sessions.stripe.com/keynote) and subsequent [breakout session](https://sessions.stripe.com/sessions/brk-01) 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](https://github.com/stripe/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](https://stripe.com/docs/stripe-cli/overview) 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](https://github.com/stripe/stripe-cli#installation) 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`



![Check the CLI version and install the apps plugin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b8wvgc1bpt56n60qqekt.png)



## 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`.


![Create the app via the CLI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k55eqww5gay7ui07qdaz.png)

## 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](https://docs.npmjs.com/package-name-guidelines), 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`

![Start the app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vov8t245mh3qfx61fjss.png)

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 example app is specifically for the customer view screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvw33ftlwx9nf8pc7kmy.png)

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](https://github.com/stripe/stripe-apps/tree/main/examples/super-todo).

![The example app, in all its glory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4towgu7uqke0wsnetqfq.png)

Many other viewports are [supported](https://stripe.com/docs/stripe-apps/reference/viewports).

## 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](https://www.youtube.com/watch?v=EfLHVQbRLEE) 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:

```tsx
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](https://webpack.js.org/concepts/hot-module-replacement/), which means updates to your app’s code can be seen in the dashboard immediately without requiring a page refresh. 

![As you update your source code, you’ll see updates to your Stripe App’s interface immediately, without a page refresh](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9kk17nvd9v7s6fei9u2.gif)

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](https://stripe.com/docs/stripe-apps/ui-toolkit/components) imported from the `ui-extension-sdk` can be used in Stripe Apps. This restriction serves two purposes: 

1. 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.
2. 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.

```tsx
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](https://stripe.com/docs/stripe-apps/ui-toolkit/components/icon#icon-reference).

Now save the file, switch to your browser and let’s see our app in action:

![All the components together form this simple app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6o2gzqvxss0hjq845e5k.gif)

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](https://twitter.com/stripedev) and [our team](https://twitter.com/i/lists/1459720002567868418) on [Twitter](https://twitter.com/StripeDev)
📺 Subscribe to our [Youtube channel](https://www.youtube.com/StripeDevelopers)
💬 Join the official [Discord server](https://discord.com/invite/RuJnSBXrQn)
📧 Sign up for the [Dev Digest](https://go.stripe.global/dev-digest)

## About the author

![Paul Asjes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7cx5dcdcibgevjqy18ja.png)

[Paul Asjes](https://twitter.com/paul_asjes) is a Developer Advocate at Stripe where he writes, codes and hosts a [monthly Q&A series](https://www.youtube.com/playlist?list=PLy1nL-pvL2M4PTst8oahVSkn_6rF0rsJ5) talking to developers. Outside of work he enjoys brewing beer, making [biltong](https://en.wikipedia.org/wiki/Biltong) and losing to his son in Mario Kart.