Migrating to Stripe Elements
Learn how to migrate your existing Stripe.js v2 checkout flow to Stripe Elements.
If you’re using Stripe.js v2 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 insert 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. If you’re using the Charges API, a Token
is returned that is then used to make a charge request or save the payment details for later. If you’re using the Payment Intents APIThe Payment Intents API is a new way to build dynamic payment flows. It tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods., you can directly call handleCardPayment to complete the payment or use createPaymentMethod to retrieve a PaymentMethod
that can be passed to your server.
Migrating to Elements requires the following steps:
Step 1: Initial setup of Elements
Elements is available as part of Stripe.js. To get started, include the following script on your pages. This script must always load directly from js.stripe.com in order to remain PCI compliant—you can’t include it in a bundle or host a copy of it yourself.
<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. Including the script on every page allows Stripe to detect anomalous behavior that may be indicative of fraud as users browse your website.
If you need to use both versions of Stripe.js for some period of time, they can be loaded and used on the same page. For example:
<script src="https://js.stripe.com/v2/"></script>
<script src="https://js.stripe.com/v3/"></script>
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" role="alert"></div>
</div>
<input type="submit" class="submit" value="Submit Payment">
</form>
Next, initialize the Stripe client by providing your publishable API key, and create an instance of Elements.
var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
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);
});
Switching to Elements requires two key changes:
- Pass the createToken() method a Stripe Element as its first argument (instead of a form or values)
- Use 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:
var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
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 Stripe.js 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 number</span></span>
</label>
<label>
<div id="card-element" class="field is-empty"></div>
<span><span>Credit or debit card</span></span>
</label>
<button type="submit">Pay $25</button>
<div class="outcome">
<div class="error" role="alert"></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_TYooMQauvdEDq54NiTphI7jx');
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);
});
Next steps
Now you have all the information you need to migrate from Stripe.js v2 to Stripe Elements. To learn more, continue reading: