Stripe Elements Migration Guide

    Learn how to migrate your existing Stripe.js checkout flow to Stripe Elements. If you need help after reading this, check out our answers to common questions or chat live with other developers in #stripe on freenode.

    If you’re using Stripe.js to securely collect card information, you’ve needed to build and configure your own input fields, validation, and formatting when creating your payment form. With Stripe Elements, our pre-built UI components, this is all handled by Stripe. Elements is fully customizable and seamlessly integrates into your checkout flow.

    Using Elements, you can create UI components and inserts them into your payment form. These components securely collect card information from your customers. When the payment form is submitted, the information is passed directly to Stripe. A Token is returned that is then used to make a charge request or save the payment details for later.

    Migrating to Elements requires the following steps:

    1. Initial setup of Elements
    2. Convert your existing payment form
    3. Securely collect card information
    4. Handle events and errors
    5. Customize style and formatting

    Step 1: Initial setup of Elements

    Elements is available as part of Stripe.js. To get started, include this script on your pages—it should always be loaded directly from https://js.stripe.com:

    <script src="https://js.stripe.com/v3/"></script>

    To best leverage Stripe’s advanced fraud functionality, include this script on every page on your site, not just the checkout page.

    Step 2: Convert your existing payment form

    At the moment, your existing payment form might look something like this:

    <form action="/charge" method="post" id="payment-form">
      <div class="card-errors"></div>
    
      <div class="form-row">
        <label>
          <span>Card number</span>
          <input type="text" size="20" data-stripe="number">
        </label>
      </div>
    
      <div class="form-row">
        <label>
          <span>Expiration (MM/YY)</span>
          <input type="text" size="2" data-stripe="exp_month">
        </label>
        <span> / </span>
        <input type="text" size="2" data-stripe="exp_year">
      </div>
    
      <div class="form-row">
        <label>
          <span>CVC</span>
          <input type="text" size="4" data-stripe="cvc">
        </label>
      </div>
    
      <div class="form-row">
        <label>
          <span>Billing Zip</span>
          <input type="text" size="6" data-stripe="address_zip">
        </label>
      </div>
    
      <input type="submit" class="submit" value="Submit Payment">
    </form>

    To use Elements, you create empty DOM elements (containers) instead of directly using DOM <input>s. Elements inserts a Stripe-hosted UI component within your container. Migrating to Elements allows you to make use of the card Element. This flexible UI component simplifies your form by minimizing the number of fields you need, requiring much less markup.

    <form action="/charge" method="post" id="payment-form">
      <div class="form-row">
        <label for="card-element">
          Credit or debit card
        </label>
        <div id="card-element">
          <!-- a Stripe Element will be inserted here. -->
        </div>
    
        <!-- Used to display form errors -->
        <div id="card-errors"></div>
      </div>
    
      <input type="submit" class="submit" value="Submit Payment">
    </form>

    Next, initialize the Stripe client by providing your publishable API key, then create an instance of Elements.

    var stripe = Stripe('pk_test_6pRNASCoBOKtIshFeQd4XMUh');
    var elements = stripe.elements();

    You can now create a card Element and add it to your page using the mount() method.

    var card = elements.create('card');
    
    // Add an instance of the card UI component into the `card-element` <div>
    card.mount('#card-element');

    Use the available style options of Elements when customizing the design of your payment form. You can also apply custom CSS to the container DOM element.

    No CSS styles should be applied directly to anything Elements inserts inside your container DOM element. Style the container DOM element instead.

    Step 3: Securely collect card details

    Using Elements to collect payment information is very similar to your current approach. The difference is that you’re passing a Stripe Element to the appropriate method, rather than a form or set of input fields.

    Your current payment form submission might look like this:

    var stripeResponseHandler = function(status, response) {
      // Grab the form:
      var form = document.getElementById('payment-form');
    
      if (response.error) { // Problem!
        // Show the errors on the form:
      } else { // Token was created!
        // Get the token ID:
        var token = response.id;
    
        // Insert the token ID into the form so it gets submitted to the server
        var form = document.getElementById('payment-form');
        var hiddenInput = document.createElement('input');
        hiddenInput.setAttribute('type', 'hidden');
        hiddenInput.setAttribute('name', 'stripeToken');
        hiddenInput.setAttribute('value', token);
        form.appendChild(hiddenInput);
    
        // Submit the form
        form.submit();
      }
    };
    
    // Create a token when the form is submitted
    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function(e) {
      e.preventDefault();
      Stripe.card.createToken(form, stripeResponseHandler);
    });

    When switching to Elements, two key changes need to be made.

    • Pass the createToken() method a Stripe Element as its first argument (instead of a form or values)
    • Make use of the Elements API to simplify the response handling logic
    function stripeTokenHandler(token) {
      // Insert the token ID into the form so it gets submitted to the server
      var form = document.getElementById('payment-form');
      var hiddenInput = document.createElement('input');
      hiddenInput.setAttribute('type', 'hidden');
      hiddenInput.setAttribute('name', 'stripeToken');
      hiddenInput.setAttribute('value', token.id);
      form.appendChild(hiddenInput);
    
      // Submit the form
      form.submit();
    }
    
    function createToken() {
      stripe.createToken(card).then(function(result) {
        if (result.error) {
          // Inform the user if there was an error
          var errorElement = document.getElementById('card-errors');
          errorElement.textContent = result.error.message;
        } else {
          // Send the token to your server
          stripeTokenHandler(result.token);
        }
      });
    };
    
    // Create a token when the form is submitted.
    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function(e) {
      e.preventDefault();
      createToken();
    });

    Step 4: Handle events and errors

    Elements includes real-time input validation that helps facilitate your users’ checkout flow, increasing conversion. If you’re not doing any validation today, you can just rely on the default visual validation indicators of Elements to alert users of errors.

    Elements also provides specific information about validation errors in real-time, helping you communicate them to your users.

    card.addEventListener('change', function(event) {
      var displayError = document.getElementById('card-errors');
      if (event.error) {
        displayError.textContent = event.error.message;
      } else {
        displayError.textContent = '';
      }
    });
    

    Step 5: Customize style and formatting

    Elements comes with built-in formatting and masking for all inputs. If you have been using Stripe’s jquery.payment library or something that you have built in-house, you should be able to safely remove these from your integration.

    Card elements can be customized to fit perfectly within your checkout page. For instance, you can make sure that the font is styled correctly by providing some additional options when creating the component:

    import Stripe from 'stripe';
    
    var stripe = Stripe('pk_test_6pRNASCoBOKtIshFeQd4XMUh');
    var elements = stripe.elements();
    
    // Create an instance of the card UI component
    var card = elements.create('card', {
      'style': {
        'base': {
          'fontFamily': 'Arial, sans-serif',
          'fontSize': '8px',
          'color': '#C1C7CD',
        },
        'invalid': {
          'color': 'red',
        },
      }
    });
    
    // Mount the UI card component into the `card-element` <div>
    card.mount('#card-element');

    You can find a full list of supported CSS properties that can be used as style parameters in our Elements reference documentation.

    Elements demo

    This is a live demo of a payment form created using Elements and custom styling. Try it out, using some of our test card information, a random three-digit CVC number, and any expiration date in the future. Upon tokenization, the contents of the form are validated before being sent to the server.

    Check out our other payment form examples to see how Elements can be customized.

    <script src="https://js.stripe.com/v3/"></script>
    <form>
      <label>
        <input name="cardholder-name" class="field is-empty" placeholder="Jane Doe" />
        <span><span>Name</span></span>
      </label>
      <label>
        <input class="field is-empty" type="tel" placeholder="(123) 456-7890" />
        <span><span>Phone</span></span>
      </label>
      <label>
        <div id="card-element" class="field is-empty"></div>
        <span><span>Card</span></span>
      </label>
      <button type="submit">Pay $25</button>
      <div class="outcome">
        <div class="error"></div>
        <div class="success">
          Success! Your Stripe token is <span class="token"></span>
        </div>
      </div>
    </form>
    * {
      font-family: 'Helvetica Neue', Helvetica, sans-serif;
      font-size: 19px;
      font-variant: normal;
      padding: 0;
      margin: 0;
    }
    
    html {
      height: 100%;
    }
    
    body {
      background: #424770;
      display: flex;
      align-items: center;
      min-height: 100%;
    }
    
    form {
      width: 480px;
      margin: 20px auto;
    }
    
    label {
      height: 35px;
      position: relative;
      color: #8798AB;
      display: block;
      margin-top: 30px;
    See all 144 lines margin-bottom: 20px; } label > span { position: absolute; top: 0; left: 0; width: 100%; height: 100%; font-weight: 300; line-height: 32px; color: #8798AB; border-bottom: 1px solid #586A82; transition: border-bottom-color 200ms ease-in-out; cursor: text; pointer-events: none; } label > span span { position: absolute; top: 0; left: 0; transform-origin: 0% 50%; transition: transform 200ms ease-in-out; cursor: text; } label .field.is-focused + span span, label .field:not(.is-empty) + span span { transform: scale(0.68) translateY(-36px); cursor: default; } label .field.is-focused + span { border-bottom-color: #34D08C; } .field { background: transparent; font-weight: 300; border: 0; color: white; outline: none; cursor: text; display: block; width: 100%; line-height: 32px; padding-bottom: 3px; transition: opacity 200ms ease-in-out; } .field::-webkit-input-placeholder { color: #8898AA; } .field::-moz-placeholder { color: #8898AA; } /* IE doesn't show placeholders when empty+focused */ .field:-ms-input-placeholder { color: #424770; } .field.is-empty:not(.is-focused) { opacity: 0; } button { float: left; display: block; background: #34D08C; color: white; border-radius: 2px; border: 0; margin-top: 20px; font-size: 19px; font-weight: 400; width: 100%; height: 47px; line-height: 45px; outline: none; } button:focus { background: #24B47E; } button:active { background: #159570; } .outcome { float: left; width: 100%; padding-top: 8px; min-height: 20px; text-align: center; } .success, .error { display: none; font-size: 15px; } .success.visible, .error.visible { display: inline; } .error { color: #E4584C; } .success { color: #34D08C; } .success .token { font-weight: 500; font-size: 15px; }
    var stripe = Stripe('pk_test_6pRNASCoBOKtIshFeQd4XMUh');
    var elements = stripe.elements();
    
    var card = elements.create('card', {
      iconStyle: 'solid',
      style: {
        base: {
          iconColor: '#8898AA',
          color: 'white',
          lineHeight: '36px',
          fontWeight: 300,
          fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
          fontSize: '19px',
    
          '::placeholder': {
            color: '#8898AA',
          },
        },
        invalid: {
          iconColor: '#e85746',
          color: '#e85746',
        }
      },
      classes: {
        focus: 'is-focused',
        empty: 'is-empty',
      },
    });
    card.mount('#card-element');
    
    See all 76 lines var inputs = document.querySelectorAll('input.field'); Array.prototype.forEach.call(inputs, function(input) { input.addEventListener('focus', function() { input.classList.add('is-focused'); }); input.addEventListener('blur', function() { input.classList.remove('is-focused'); }); input.addEventListener('keyup', function() { if (input.value.length === 0) { input.classList.add('is-empty'); } else { input.classList.remove('is-empty'); } }); }); function setOutcome(result) { var successElement = document.querySelector('.success'); var errorElement = document.querySelector('.error'); successElement.classList.remove('visible'); errorElement.classList.remove('visible'); if (result.token) { // Use the token to create a charge or a customer // https://stripe.com/docs/charges successElement.querySelector('.token').textContent = result.token.id; successElement.classList.add('visible'); } else if (result.error) { errorElement.textContent = result.error.message; errorElement.classList.add('visible'); } } card.on('change', function(event) { setOutcome(event); }); document.querySelector('form').addEventListener('submit', function(e) { e.preventDefault(); var form = document.querySelector('form'); var extraDetails = { name: form.querySelector('input[name=cardholder-name]').value, }; stripe.createToken(card, extraDetails).then(setOutcome); });