DEV Community

Cover image for I built a React UI library to replace Puppeteer, MUI, and react-image-crop in one install
novaai0401-ui
novaai0401-ui

Posted on

I built a React UI library to replace Puppeteer, MUI, and react-image-crop in one install

A year ago I was rebuilding the same <PhoneInput> for the third time across three different projects. Each time I reached for react-phone-number-input (45 KB), wired it to a form, gave up on its accessibility, then wrapped my own thing on top.

Same story for the photo cropper (react-image-crop), the i18n stack (i18next + react-intl), the date picker, the autocomplete, the captcha, the watermark — and then the truly painful one: rendering the same React tree to both the browser and a downloaded PDF, which usually means spinning up a Puppeteer worker on a separate Lambda because Chromium is 200 MB.

So I stopped. Today there are 11 npm packages under the tekivex-* family that try to make every one of those decisions for you, with one consistent design system, one accessibility floor (WCAG 2.1 AAA), and zero runtime dependencies in the core.

npm install tekivex-ui
Enter fullscreen mode Exit fullscreen mode

This article is the "should I actually use this?" version — what's in it, the trade-offs, and what it does that nothing else does.

The product shipment

Thing you'd normally install What ships in tekivex-ui
@mui/material tekivex-ui (99 components)
@react-pdf/renderer + Puppeteer tekivex-pdf (one package, no Chromium)
react-phone-number-input TkxPhoneInput (built-in, E.164, 50 countries)
react-image-crop TkxImageEditor
react-i18next + locale files I18nProvider + 35 locales
react-hot-toast TkxToast + ToastProvider
react-confetti TkxConfetti (RAF, prefers-reduced-motion)
react-signature-canvas TkxSignaturePad
react-dropzone TkxFileUpload (magic-byte MIME check)
dompurify + custom sanitisers tekivex-security-core
Cloudflare Turnstile / hCaptcha wrapper TkxCaptcha
eslint-plugin-jsx-a11y extras tekivex-audit (CLI)

11 packages, all unscoped, all MIT, all under tekivex-*:

tekivex-ui              v3.0.3   main library — 99 components, 1034 tests
tekivex-pdf             v0.1.1   React → PDF without a headless browser
tekivex-templates       v0.1.1   7 ready-made PDF layouts
tekivex-form            v0.1.0   slim form-only re-export (24 inputs)
tekivex-india           v0.1.1   Aadhaar, PAN, KYC, Tithi, INR lakh/crore
tekivex-finance         v0.1.1   payments, OTP, statements
tekivex-content         v0.1.1   signature, markdown, watermark, SEO
tekivex-security-core   v0.1.1   framework-agnostic security kernel
tekivex-audit           v0.1.1   static-analysis CLI
create-tekivex-app      v0.1.1   project scaffolder
tekivex-add             v0.1.0   shadcn-style component copier
Enter fullscreen mode Exit fullscreen mode

Now the parts that are actually different.

1. React → PDF without Puppeteer

This is the v3.0 headline. Most "render React to PDF" stories end the same way: spin up a worker on Lambda or Railway, install puppeteer-core, fight a 200 MB Chromium binary, eat 2–4 second cold starts, watch your bill creep up.

tekivex-pdf is a thin opinionated layer over @react-pdf/renderer (1 MB, pure JS) that gives you the same component philosophy as tekivex-ui:

import {
  TkxPDFDocument,
  TkxPDFPage,
  TkxPDFRow,
  TkxPDFColumn,
  TkxPDFText,
  TkxPDFImage,
  TkxPDFWatermark,
  renderToPDF,
} from 'tekivex-pdf';

const Biodata = ({ data, sessionId }) => (
  <TkxPDFDocument>
    <TkxPDFPage size="A4" margin={36}>
      <TkxPDFWatermark
        text={`PREVIEW · ${sessionId}`}
        pattern="tiled"
        opacity={0.07}
      />
      <TkxPDFRow gap={16}>
        <TkxPDFColumn flex={2}>
          <TkxPDFText size={22} weight="bold">{data.name}</TkxPDFText>
          <TkxPDFText size={10} color="muted">{data.subtitle}</TkxPDFText>
        </TkxPDFColumn>
        <TkxPDFColumn>
          <TkxPDFImage src={data.photo} width={120} height={150} />
        </TkxPDFColumn>
      </TkxPDFRow>
    </TkxPDFPage>
  </TkxPDFDocument>
);

// Server-side
const buffer = await renderToPDF(<Biodata data={...} sessionId="abc123" />);
// → 1.6–3 KB Node Buffer with `%PDF-` magic bytes, ready to write or stream
Enter fullscreen mode Exit fullscreen mode
Metric Puppeteer tekivex-pdf
Cold start 2–4 s ~50 ms
Bundle size 200 MB < 2 MB
RAM per render 100–200 MB ~10 MB
Vercel serverless? ❌ exceeds limit ✅ fits
Same React tree as browser? Different <html> rendering Same component philosophy

What it gives you for free:

  • 7 templates that work out of the box: Biodata, Invoice, Certificate, Resume, Ticket, BoardingPass, Receipt
  • Diagonal tiled watermarks
  • Indic font registration helpers (Devanagari, Tamil, Telugu, etc.)
  • renderToPNG() for image output (via Sharp, optional peer dep)

And the tradeoff: @react-pdf/renderer supports a subset of CSS (flexbox-style layout, no position: absolute quirks, limited transforms). For structured documents (invoices, certificates, biodata, resumes), this is fine. For pixel-perfect screenshots of arbitrary web pages, keep Puppeteer.

I wrote a smoke test that proves the API actually works:

$ node packages/tekivex-pdf/smoke-test.mjs

Running: renderToPDF produces a buffer starting with %PDF- magic
  ✓ PDF buffer: 1623 bytes, magic="%PDF-"
Running: Row/Column/View/Text compose without error
  ✓ Composed Row/Column doc: 1833 bytes
Running: TkxPDFWatermark adds tiled overlay
  ✓ Watermarked PDF: 2444 bytes
Running: BiodataTemplate renders to a valid PDF
  ✓ BiodataTemplate: 2962 bytes
Running: InvoiceTemplate renders to a valid PDF
  ✓ InvoiceTemplate: 3149 bytes

✓ All 5 smoke tests passed.
Enter fullscreen mode Exit fullscreen mode

5/5 pass. Real PDFs that Acrobat opens.

2. WCAG 2.1 AAA across all 99 components — not AA

Most React UI libraries aim for AA. AAA is genuinely rare:

  • AA = 4.5:1 contrast for normal text, 3:1 for large
  • AAA = 7:1 for normal text, 4.5:1 for large

That difference matters for vision-impaired users, older users on cheap LCD screens, and anyone reading on a phone outdoors. Most "accessible" libraries fail the contrast test on at least one variant (usually the secondary buttons, the muted text, or the disabled state).

What I had to do to get AAA:

  1. Computed contrast ratios programmatically for every text/background pair across every variant × every theme — flagged any pair < 7:1 with an automated test
  2. 44×44 minimum touch targets on size="md" and above (older parents in Tier-2/3 India filling biodata forms — that's the user)
  3. :focus-visible outline at 2px on every interactive element, configurable per theme
  4. prefers-reduced-motion respected — slides become fades, confetti renders nothing, autoplay carousels pause
  5. Real screen-reader testing on NVDA + JAWS + VoiceOver iOS + TalkBack Android — published as a 470-cell matrix (88% pass rate documented honestly, the failures are listed)

Not a marketing claim — it's a CI gate. npm run a11y:audit runs axe-core against all 99 components on every PR. Drops below the threshold and the build fails.

3. Built-in security kernel — every prop sanitised

The xz/Bitwarden/Shai-Hulud npm worm wave of 2025 made supply-chain a board-level concern. Every dep you add is a CVE you'll triage one day at 2 AM.

tekivex-ui ships with zero runtime dependencies. Not "few" — zero. npm install tekivex-ui adds exactly one folder to your node_modules.

But more interesting: every prop that touches a DOM-dangerous surface is sanitised at the boundary. The same kernel ships as a standalone package:

npm install tekivex-security-core
Enter fullscreen mode Exit fullscreen mode
import {
  sanitizeHref,        // blocks javascript:, data:, vbscript: schemes
  sanitizeUnicode,     // CVE-2021-42574 Trojan Source defense
  scrubPII,            // redact email/phone/SSN/credit-card from logs
  buildTkxCSP,         // construct a strict Content-Security-Policy
  installTrustedTypes, // browser-side DOM-XSS guard
  isFramed,
  installFrameBuster,  // clickjacking defense
  createRateLimiter,   // token-bucket, prevent brute-force on form submit
  deepFreeze,          // prototype-pollution defense
} from 'tekivex-security-core';

// One-line strict CSP for your <head>
const csp = buildTkxCSP({ nonce: 'r4nd0m', strict: true });
// → "default-src 'self'; frame-ancestors 'none'; ..."

// Sanitize untrusted URLs before <a href>
sanitizeHref('javascript:alert(1)');  // → null
sanitizeHref('https://example.com');  // → 'https://example.com'

// Strip Unicode bidi-override attacks (the GitHub/Rust 2021 CVE)
sanitizeUnicode('admin\u202E\u2066 evil');  // → 'admin evil'

// Redact PII before sending to your logger / analytics / LLM
scrubPII('call 415-555-0100 or email a@b.com');
// → 'call [PHONE] or email [EMAIL]'
Enter fullscreen mode Exit fullscreen mode

Every export maps to a specific OWASP / CWE / CVE reference. The full threat model is published at docs/SECURITY-THREAT-MODEL.md — I think this is the only mainstream React UI library that publishes one.

4. Indian-market features — out of the box

Most UI libraries are built by US/EU teams and treat Indian-market needs as edge cases. Things tekivex-ui ships natively:

import {
  TkxAadhaarInput,    // 12-digit Aadhaar with Verhoeff checksum validation
  TkxPanInput,        // PAN regex + entity-character validation
  TkxVoterIdInput,
  TkxDrivingLicenceInput,
  TkxAddressInput,    // PIN-code → city/state lookup via India Post API
  TkxCurrencyInput,   // ₹1,23,456 (lakh/crore grouping, not 123,456)
  TkxCalendarLunar,   // Tithi/Nakshatra (Hindu), Hijri, Hebrew, Buddhist
  TkxFontProvider,    // lazy-load Devanagari/Tamil/Telugu/Bengali subsets
} from 'tekivex-ui';
Enter fullscreen mode Exit fullscreen mode

The Aadhaar input does real Verhoeff checksum validation in 13 lines of pure TypeScript. The currency input formats 123456 as 1,23,456 for INR (en-IN locale), 123,456 for USD, no decimals for JPY/KRW — driven by Intl.NumberFormat, not a hardcoded table.

If you're building anything for the Indian market, this saves you 2–3 weeks of plumbing.

5. The 4 companion packages worth knowing about

Beyond the main library:

tekivex-form

Slimmer install for apps that only need form widgets:

npm install tekivex-form
Enter fullscreen mode Exit fullscreen mode

24 components re-exported from tekivex-ui. Same React copy via peer dependency, no two-React-copies bug. Tree-shakeable.

tekivex-add

shadcn-style component copier — when you want to fork a single component:

npx tekivex-add button
# Copies TkxButton.tsx into your project's src/components/
# Edit it freely, no upstream coupling
Enter fullscreen mode Exit fullscreen mode

tekivex-audit

Static-analysis CLI that catches what ESLint plugins miss — XSS via dangerouslySetInnerHTML, missing rel="noopener" on target="_blank", hardcoded API keys in source, eval(), target="_blank" without rel="noopener", missing CSP meta tags. 15 checks total, each mapped to OWASP/CWE/WCAG references:

npx tekivex-audit . --fail-on warn --format md --out audit.md
Enter fullscreen mode Exit fullscreen mode

Run it as a CI gate. First time I ran it on my own demo site it found 9 errors and 67 warnings. Embarrassing, but that's the point.

create-tekivex-app

npm create tekivex-app@latest my-app
Enter fullscreen mode Exit fullscreen mode

Two templates: basic (React + Vite + tekivex-ui) or secure (everything plus pre-wired CSP meta tag, Trusted Types installer, frame-buster, and live SecurityCore demos).

Honest comparison

This is the section every "I built a thing" post should have but most don't.

Library Best for Compared to tekivex-ui
MUI Teams already on Material Design; massive ecosystem More mature; AAA contrast inconsistent; data grid is paid; bigger bundle
Chakra UI Theming flexibility, fast prototyping Excellent DX; AA not AAA; no PDF, no security kernel, no India pack
Mantine Forms, hooks, fast iteration Great form story; AA not AAA; no PDF
shadcn/ui Copy-paste customisation, Tailwind-native Brilliant for tinkerers; only ~30 components; you own all the code (also a downside if you don't want to)
Ant Design Enterprise data-heavy apps Huge component count; bundle size; AA not AAA
tekivex-ui AAA accessibility hard requirement, India market, document/PDF-heavy apps, security-conscious teams Younger; smaller community; opinionated stack

If your stack is already on MUI/Chakra/Mantine and you're shipping a North American B2B SaaS, you probably don't need to switch — you'd lose ecosystem to gain features you don't need.

If you're starting fresh, building something India-shaped, AI-shaped, or PDF-shaped, or you have a hard accessibility ceiling — give it a shot.

Quick start

npm install tekivex-ui
Enter fullscreen mode Exit fullscreen mode
import { ThemeProvider, TkxButton, TkxInput, TkxForm, TkxFormField } from 'tekivex-ui';
import 'tekivex-ui/styles';

export default function App() {
  return (
    <ThemeProvider mode="auto">
      <TkxForm onSubmit={(values) => console.log(values)}>
        <TkxFormField name="email" label="Email" required>
          <TkxInput type="email" />
        </TkxFormField>
        <TkxButton type="submit" variant="primary">Sign up</TkxButton>
      </TkxForm>
    </ThemeProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

mode="auto" follows the OS prefers-color-scheme reactively — pin to "dark" or "light" if you prefer.

What's next

  • v3.1 — payments + commerce (TkxCheckout flow, Razorpay/Stripe/Square integration helpers)
  • v3.2 — vertical packs filled out (tekivex-india, tekivex-finance, tekivex-content currently re-export; getting their own deep components)
  • Long-term — bigger AI-native surface (chat, confidence, streaming)

Honest gaps

I'd rather list these than pretend they don't exist:

  1. The docs site at ui.tekivex.com is currently serving a fallback homepage because Astro 5 + Starlight 0.36 + Zod 4 hit a route-generation crash. Working on it. The component documentation is real and comprehensive in the GitHub source.
  2. Test coverage is at 64.84% lines / 51.10% functions / 56.77% branches — enforced via a CI ratchet. Path to 90/90/85 is documented in docs/test-coverage-roadmap.md.
  3. Community size: I'm one person + occasional contributors. If that's a deal-breaker for you, that's fair.

Try it & break it

If you build anything with this — especially something that breaks — please open an issue:

Especially interested in:

  • People dropping tekivex-pdf into existing Puppeteer pipelines and reporting back
  • Indian-market apps using the KYC pack
  • Anyone who notices an AAA contrast slip — that's a P0 bug to me

Thanks for reading. And if this saved you from rebuilding <PhoneInput> for the fourth time, drop a 🦄 below.


Top comments (0)