- No data uploaded
- Instant results
Tailwind CSS Razorpay Checkout Form for Vanilla JS — Glassmorphism
A production-ready glassmorphism razorpay checkout form: a two-column checkout with an order summary (subtotal, GST, total in INR), contact fields, a UPI / card / netbanking selector and a pay button that opens Razorpay Checkout. Pick your brand color, toggle dark mode and corner radius — the live preview and the Vanilla JS code update instantly in your browser.
Live component editor
<!-- Requires Tailwind CSS v3 (CLI/PostCSS build, or the Play CDN for prototypes) -->
<div class="w-full max-w-4xl grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div class="h-full p-6 bg-white/60 backdrop-blur-xl border border-white/60 shadow-2xl rounded-xl">
<h2 class="text-lg font-semibold text-slate-900">Order summary</h2>
<div class="mt-4 flex items-start justify-between gap-4">
<div>
<p class="font-medium text-slate-900">Pro plan · annual</p>
<p class="text-sm text-slate-500">Billed once, renews yearly</p>
</div>
<p class="font-medium text-slate-900">₹8,474</p>
</div>
<div class="mt-6 pt-4 flex flex-col gap-2 text-sm border-t border-slate-200">
<div class="flex justify-between text-slate-500"><span>Subtotal</span><span>₹8,474</span></div>
<div class="flex justify-between text-slate-500"><span>GST (18%)</span><span>₹1,525</span></div>
<div class="flex justify-between text-base font-semibold text-slate-900"><span>Total</span><span>₹9,999</span></div>
</div>
<p class="mt-6 text-xs text-slate-500">Secured by Razorpay · PCI DSS compliant</p>
</div>
</div>
<div>
<form class="h-full p-6 flex flex-col gap-4 bg-white/60 backdrop-blur-xl border border-white/60 shadow-2xl rounded-xl">
<h2 class="text-lg font-semibold text-slate-900">Payment details</h2>
<div>
<label for="rp-email" class="block mb-1.5 text-sm font-medium text-slate-700">Email</label>
<input id="rp-email" type="email" autocomplete="email" placeholder="you@company.com" class="block w-full px-3 py-2 text-sm rounded-lg border bg-white/70 border-white/80 text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:border-[#4f46e5]" required>
</div>
<div>
<label for="rp-phone" class="block mb-1.5 text-sm font-medium text-slate-700">Mobile number</label>
<input id="rp-phone" type="tel" inputmode="tel" autocomplete="tel" placeholder="+91 98765 43210" class="block w-full px-3 py-2 text-sm rounded-lg border bg-white/70 border-white/80 text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:border-[#4f46e5]" required>
</div>
<div>
<p class="block mb-1.5 text-sm font-medium text-slate-700">Payment method</p>
<div class="flex flex-wrap gap-2" role="group" aria-label="Payment method">
<button type="button" class="inline-flex items-center justify-center px-3 py-2 text-sm font-semibold rounded-lg border transition-colors border-white/80 bg-white/40 text-slate-700 hover:bg-white/70 aria-pressed:border-[#4f46e5] aria-pressed:text-[#4f46e5] aria-pressed:ring-1 aria-pressed:ring-[#4f46e5]" data-set="method:upi" data-pressed="method:upi" aria-pressed="true">UPI</button>
<button type="button" class="inline-flex items-center justify-center px-3 py-2 text-sm font-semibold rounded-lg border transition-colors border-white/80 bg-white/40 text-slate-700 hover:bg-white/70 aria-pressed:border-[#4f46e5] aria-pressed:text-[#4f46e5] aria-pressed:ring-1 aria-pressed:ring-[#4f46e5]" data-set="method:card" data-pressed="method:card" aria-pressed="false">Card</button>
<button type="button" class="inline-flex items-center justify-center px-3 py-2 text-sm font-semibold rounded-lg border transition-colors border-white/80 bg-white/40 text-slate-700 hover:bg-white/70 aria-pressed:border-[#4f46e5] aria-pressed:text-[#4f46e5] aria-pressed:ring-1 aria-pressed:ring-[#4f46e5]" data-set="method:netbanking" data-pressed="method:netbanking" aria-pressed="false">Netbanking</button>
</div>
<p class="mt-2 text-xs text-slate-500" data-when="method:upi">Pay with any UPI app — GPay, PhonePe, Paytm or BHIM.</p>
<p class="mt-2 text-xs text-slate-500" data-when="method:card" hidden>Visa, Mastercard, RuPay and Amex credit or debit cards.</p>
<p class="mt-2 text-xs text-slate-500" data-when="method:netbanking" hidden>All major Indian banks supported.</p>
</div>
<button type="button" class="inline-flex items-center justify-center px-4 py-2.5 text-sm font-semibold rounded-lg bg-[#4f46e5] hover:bg-[#433cc3] text-white shadow-sm transition-colors focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:ring-offset-2 focus:ring-offset-white w-full" data-call="payWithRazorpay">Pay ₹9,999</button>
</form>
</div>
</div>
<script>
(() => {
// Razorpay Standard Checkout — https://razorpay.com/docs/payments/payment-gateway/web-integration/standard/
const RAZORPAY_KEY_ID = 'rzp_test_YOUR_KEY_ID'; // public key only — the secret stays on your server
function loadRazorpay() {
if (window.Razorpay) return Promise.resolve();
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = 'https://checkout.razorpay.com/v1/checkout.js';
s.onload = resolve;
s.onerror = () => reject(new Error('Razorpay SDK failed to load'));
document.head.appendChild(s);
});
}
async function payWithRazorpay() {
await loadRazorpay();
// 1) Server creates the order (amount in paise) — never trust a client-side amount.
const order = await fetch('/api/razorpay/order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan: 'pro-annual' })
}).then((r) => r.json());
// 2) Open Checkout for that order.
const rzp = new window.Razorpay({
key: RAZORPAY_KEY_ID,
order_id: order.id,
amount: order.amount,
currency: order.currency,
name: 'Acme Inc.',
description: 'Pro plan — annual',
prefill: {
email: document.getElementById('rp-email')?.value,
contact: document.getElementById('rp-phone')?.value
},
theme: { color: '#4f46e5' },
// 3) Server verifies razorpay_signature before fulfilling.
handler: (res) =>
fetch('/api/razorpay/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(res) })
});
rzp.on('payment.failed', (res) => console.error(res.error));
rzp.open();
}
const state = { method: "upi" };
const actions = { payWithRazorpay };
const pair = (s) => s.split(':');
const sync = () => {
document.querySelectorAll('[data-show]').forEach((el) => (el.hidden = !state[el.dataset.show]));
document.querySelectorAll('[data-hide]').forEach((el) => (el.hidden = !!state[el.dataset.hide]));
document.querySelectorAll('[data-when]').forEach((el) => { const [k, v] = pair(el.dataset.when); el.hidden = state[k] !== v; });
document.querySelectorAll('[data-pressed]').forEach((el) => { const [k, v] = pair(el.dataset.pressed); el.setAttribute('aria-pressed', String(state[k] === v)); });
document.querySelectorAll('[data-expanded]').forEach((el) => el.setAttribute('aria-expanded', String(!!state[el.dataset.expanded])));
};
document.addEventListener('click', (e) => {
const t = e.target.closest('[data-toggle],[data-open],[data-close],[data-set],[data-call]');
if (!t) return;
const { toggle, open, close, set, call } = t.dataset;
if (call) actions[call]?.(e);
if (toggle) state[toggle] = !state[toggle];
if (open) state[open] = true;
if (close) state[close] = false;
if (set) { const [k, v] = pair(set); state[k] = v; }
sync();
});
sync();
})();
</script>
More Tailwind components for Vanilla JS
All components →- Currency Pricing Table A dynamic pricing section that detects the visitor’s currency, shows purchasing-power-adjusted regional prices and lets users switch between USD, EUR, GBP, INR and JPY
- Pricing Table A three-tier pricing section with a monthly/yearly billing toggle, a highlighted “most popular” plan and feature checklists
- Signup Form A registration card with name, email and password fields, a password hint, a terms-of-service checkbox and a primary call to action
- Cookie Consent Banner A GDPR-style cookie banner with equally prominent “Accept all” and “Reject all”, granular analytics/marketing toggles (off by default), stored consent and Google Consent Mode v2 signals
- FAQ Accordion A frequently-asked-questions section with four expandable items, each toggled by a button that exposes its state via aria-expanded
- Login Modal A centered sign-in dialog with email and password fields, “remember me”, a forgot-password link and a social sign-in button
- Navbar A responsive top navigation bar with a logo, primary links, a sign-in/CTA pair on desktop and a collapsible hamburger menu on mobile
- Hero Section A landing-page hero with an announcement badge, a large headline, supporting copy, primary and secondary calls to action and a row of social-proof stats
Install Tailwind CSS for Vanilla JS
Prototype instantly with the Play CDN, or install the Tailwind CLI for production:
<script src="https://cdn.tailwindcss.com"></script>npm install -D tailwindcss@3
npx tailwindcss init
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watchAdd ./**/*.html to content in tailwind.config.js and put the three @tailwind directives in src/input.css.
Use the razorpay checkout form in Vanilla JS
Paste the markup where the component should appear. The small inline <script> wires up the interactive state with one delegated click listener — no framework needed.
Connecting the form to Razorpay
- Create an order on your server with the Orders API using your
key_secretand return{ id, amount, currency }from/api/razorpay/order. - Replace
rzp_test_YOUR_KEY_IDwith your public key_id (test keys first). - In
/api/razorpay/verify, computeHMAC_SHA256(order_id + "|" + payment_id, key_secret)and compare it withrazorpay_signaturebefore marking the order paid. Also handle thepayment.capturedwebhook as the source of truth.
The preview never contacts Razorpay — clicking “Pay” there only shows where checkout would open.
Accessibility & customization
Payment methods are toggle buttons with aria-pressed, totals are real text (not images), the phone field uses inputmode="tel" and autocomplete tokens, and the pay button states the exact amount.
Every color, radius and dark-mode choice is baked into plain utility classes, so the copied code has no runtime dependency on this site. The glass style relies on backdrop-filter, supported in all current browsers; place it over a colorful background for the best effect.
Compatibility
| Item | Details |
|---|---|
| CSS framework | Tailwind CSS v3.4+ |
| Library | Plain HTML + optional vanilla JS |
| Interactive state | method |
| Dark mode | Yes — toggle in the editor |
| Style | Glassmorphism (backdrop blur) |