paypal-checkout-components: the component set behind PayPal Buttons
please submit Issues about the PayPal JS SDK here: https://github.com/paypal/paypal-js/issues
At a glance
- What is it?
- paypal-checkout-components is the Apache licensed package that renders PayPal Buttons and the checkout experience on a merchant site, built on the zoid component framework. It is published to npm every Wednesday, it treats the size of the gzipped bundle as a test, and it renders twice, once in the browser and once on the server, with a global flag deciding which path runs.
- Who is it for?
- paypal-checkout-components fits a merchant integrating PayPal Checkout who wants the official components and the official update channel rather than a hand rolled button, and who is prepared to treat the SDK as a PayPal owned dependency.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A component library for a hosted checkout
This repository holds a set of components allowing easy integration of PayPal Buttons and PayPal Checkout into your site, powered by zoid.
The name of the framework is worth pausing on, because it explains the shape of the code. zoid is a component framework from the KrakenJS ecosystem, and it is the same kind of tool that lets a hosted widget, such as a payment provider's button, be rendered as if it were a local component. That is what makes the two phase render in this package possible, and it is why the buttons are elements in your markup rather than an iframe you assemble by hand.
The developer documentation lives at developer.paypal.com/docs/checkout, and the package is published to npm as @paypal/checkout-components, at version 5.0.437 in the repository's package manifest.
There is one routing note in the README that saves a wasted issue tracker. Please submit any issues about the JS SDK to the paypal-js repository. This repository is the components; the SDK that loads and configures them lives elsewhere.
The licence is Apache 2.0, the default branch is main, and the repository was last pushed to on 2026-10-14.
Two renders, one __WEB__ flag, and a deliberate CDN for the logos
The logo strategy section is the most interesting engineering description in the README, and it explains an implementation that would otherwise look odd.
There are two phases. In the first render, the buttons are rendered on the client side inside an iframe tag with no src attribute, using the code bundled inside the JS SDK script. A global variable called __WEB__ tells the code whether it is rendering client side or server side, and it is true in the browser.
To minimise the bundle size of the JS SDK script, the SVG logos are not in the bundle at all in that phase. They are loaded from the www.paypalobjects.com CDN as external images, through a PayPalLogoExternalImage component.
In the second phase, the render happens on the server. The iframe tag is fully rendered by setting the src attribute and making an http request to PayPal's servers on www.paypal.com to populate the contents, which are securely hosted. The same code in this repository renders the buttons server side, and to make sure there are no issues with the logos, the SVG code is inlined for this render, through a PayPalLogoInlineSVG component, with __WEB__ set to false so the server code path executes.
So the two phases make opposite decisions about the same asset. On the client, the logo is a URL to a CDN, because the bundle size matters most there. On the server, the logo is inlined, because a remote image would be an extra request and might not render at all in that context.
That asymmetry is a performance decision made in two places, and the global flag is what keeps it in one codebase.
The test suite is six tasks, and npm test runs all of them
The development section is organised around the six testing tasks, and the top level command runs all of them.
npm testThat covers lint, flow, karma, jest-ssr, jest-screenshot and check-size, and there are flags for the whole run: --clear-cache to clear the Babel Loader and PhantomJS cache, --debug for debug mode across PhantomJS, Karma and CheckoutJS, --quick for the fastest run with minimal output and no coverage, and --browser to choose the browser.
Each task is a separate concern. The lint task runs eslint with definitions extended from the Grumbler-Scripts configuration, and it has a fix variant. The flow task checks typing issues with Flow, and the README notes that flow-typed has to be run first to generate the type interfaces for the third party libraries, which is a detail that bites the first time. The karma task runs the browser tests with the Mocha framework and is responsible for the test/e2e and test/integration directories.
Then there are the two Jest tasks. jest-ssr checks for the correct rendering of components on the server side, which is the automated half of the two phase render story. And jest-screenshot uses Puppeteer and Jest to take screenshots and check them against existing views to look for discrepancies, with the tests defined in test/screenshot/config.js.
A screenshot diff in a payments component library is not a luxury. The buttons have to look identical in every browser and in both render paths, because a visual regression in a checkout is a support ticket and a lost sale, and a screenshot test is the only thing that catches a one pixel shift in a logo.
Bundle size is a test, and a demo folder that fakes a domain
The check-size task checks that the compiled and gzipped bundle does not exceed the recommended size limit, and in the package scripts it is more than a check. It runs a separate size build, feeds the result to bundlemon, and then deletes the artefacts, with a .bundlemonrc.json configuration in the repository holding the budgets.
That is a size budget enforced in continuous integration rather than a comment in a design document, and for a package that every merchant page loads it is the difference between a component library and a performance problem. The repository also has a webpack.config.size.js, which exists to produce the artefact that is measured, and a .percy.yml for visual review.
The local development setup is unusual in a way that is worth understanding. The dev script starts a webpack dev server on port 9001, with the host set to localhost.paypal.com, with https enabled, and serving the demo directory as static content.
The host is the point. Because the buttons render inside an iframe and the server side render makes requests to PayPal's servers, the local environment has to look like the real thing, and localhost.paypal.com is a hostname that resolves to the local machine. The demo folder carries the same idea, with separate pages for angular.htm, angular2.htm, button.htm, card.htm, react.htm and vue.htm, so each framework integration has a page that can be opened during development.
The scripts are all npm scripts, with check-node-version as a gate before the dev server starts, and a separate demo script that serves the demo directory on port 1337 for a quicker look.
Weekly publishing, and a release list that stopped in 2018
The releasing section says this package is published weekly, every Wednesday, and points at the changelog to stay updated with bug fixes and new features.
That is a useful commitment to know about, because it is the opposite of the versioning story the repository tells by accident. The published releases in the repository metadata are v4.0.234 from November 2018, v4.0.233 and v4.0.232 from the same week, while the package manifest says 5.0.437. So the GitHub releases list has not moved since 2018 while npm has moved on by a major version and hundreds of patches.
The practical consequence is that the GitHub tags are not a usable version history, and the npm version is the real one. If you are pinning, pin the npm version, and if you are reading the changelog to work out what changed, read the CHANGELOG.md rather than the releases page, because the release page is frozen at the moment the project stopped using it.
The repository also has a .husky/ directory, so there are git hooks in the development flow, an .nvmrc and a check-node-version script, which suggests the Node version is pinned for a reason, and a SECURITY.md for the disclosure path.
Editorial conclusion
paypal-checkout-components fits a merchant integrating PayPal Checkout who wants the official components and the official update channel rather than a hand rolled button, and who is prepared to treat the SDK as a PayPal owned dependency. It does not fit a team that needs a checkout flow it controls end to end, since the components render PayPal hosted pages, and it does not fit anyone who wants a stable version history from GitHub, because the published releases stop in 2018 while npm is far ahead. Verify three things before you adopt it: which npm version you are pinning, since the GitHub tags are not a usable version history, that your build treats three as an external rather than bundling its own copy, and that the server side render path is configured, because the two phase render means the markup you get from a server differs from what the client produces and the tests cover both.
Frequently asked questions
What is in the paypal-checkout-components repository?
A set of components for integrating PayPal Buttons and PayPal Checkout into a site, powered by the zoid component framework, published to npm as @paypal/checkout-components with version 5.0.437 in the repository. The developer documentation is at developer.paypal.com/docs/checkout, and issues about the JS SDK itself belong in the paypal-js repository instead.
How do I run the tests for the PayPal checkout components?
npm test runs all six testing tasks, which are lint, flow, karma, jest-ssr, jest-screenshot and check-size. There are flags for the whole run, --clear-cache, --debug, --quick for minimal output without coverage, and --browser to pick a browser, and each task also has its own npm script.
How does the two phase render of the PayPal buttons work?
In the client side phase the buttons are rendered inside an iframe with no src, and the SVG logos are loaded from the www.paypalobjects.com CDN as external images to keep the JS SDK bundle small. In the server side phase the iframe src is set and the contents are fetched from PayPal's servers, and the SVG code is inlined instead, with a __WEB__ global flag deciding which code path runs.
Does the project enforce a bundle size limit?
Yes. The check-size task checks that the compiled and gzipped bundle does not exceed the recommended size limit, and in the package scripts it runs a separate size build, passes the result to bundlemon and then removes the artefacts, with the budgets held in a .bundlemonrc.json configuration.
How often is the package released, and which version numbers matter?
The README says the package is published weekly, every Wednesday, and points at the CHANGELOG for the changes. The published GitHub releases in the repository stop at v4.0.234 from 2018, while the npm version is 5.0.437, so the npm version is the one to pin and the changelog is the one to read.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/paypal-paypal-checkout-components)Was this useful?