npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@clndr-pro/react

v0.2.0

Published

React components for embedding clndr.pro booking flows.

Downloads

58

Readme

@clndr-pro/react

React components + hooks for clndr.pro. See the repo README for full docs.

'use client';
import { ClndrProvider, BookingInline } from '@clndr-pro/react';

<ClndrProvider publishableKey={process.env.NEXT_PUBLIC_CLNDR_PUBLISHABLE_KEY!}>
  <BookingInline slug="30-min-intro" />
</ClndrProvider>;

Markdown descriptions, rendered for you

Booking pages on clndr.pro author their descriptions in markdown. @clndr-pro/react renders them out of the box — headings, lists, links, GFM tables, code blocks, the lot — so you don't have to wire up react-markdown yourself. The default uses semantic shadcn/Tailwind tokens (text-primary, bg-muted, border-border, text-muted-foreground), so the description picks up your theme automatically.

The renderer is lazy-loaded via a separate ESM chunk. If your booking pages don't carry a description, or you pass your own renderer, your consumers never download react-markdown + remark-gfm (~50 KB).

Use the default

Nothing to do — <BookingForm>, <BookingInline>, and <BookingModal> all render bookingPage.description automatically when present.

Override with your own renderer

If you already ship a <Markdown> (MDX, content collection, custom prose component, whatever), pass it through components:

import { BookingInline } from '@clndr-pro/react';
import { MyMarkdown } from '@/components/markdown';

<BookingInline
  slug="30-min-intro"
  components={{
    MarkdownRenderer: ({ content, className }) => (
      <MyMarkdown source={content} className={className} />
    ),
  }}
/>;

Your renderer's chunk is bundled normally; the default chunk is never requested.

Customize the wrapper

The wrapper around the rendered markdown takes a class via classNames.description. Default is clndr-description. Drop a Tailwind prose class for a different layout:

<BookingInline
  slug="30-min-intro"
  classNames={{ description: 'prose prose-zinc dark:prose-invert max-w-none mb-4' }}
/>;

Customization

The React components ship with minimal default markup. Every element can be replaced via the components prop, and every class name overridden via classNames. This makes them drop-in compatible with Shadcn UI, MUI, Chakra, or any other design system.

import { BookingForm } from '@clndr-pro/react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';

<BookingForm
  slug="30-min-intro"
  components={{
    Button,
    Input,
    Label,
    Card: (props) => <div className="rounded-lg border p-4" {...props} />,
  }}
  classNames={{
    buttonPrimary: 'w-full',
    slotGrid: 'grid grid-cols-3 gap-2',
  }}
/>;

Or go even lower-level with hooks:

import { useBookingPage, useAvailableSlots, useCreateBooking } from '@clndr-pro/react';

const { data: page } = useBookingPage(slug);
const { slots } = useAvailableSlots(slug, date);
const { create, isLoading } = useCreateBooking();

Slot reference

<BookingForm> accepts a components prop with these slots — each one is optional and falls back to a minimal default:

| Slot | Default | Purpose | | ------------------ | -------------------------------- | ----------------------------------------------------------- | | Root | <div> | Outer container | | Card | <div> | Form / confirmation card | | Heading | <h2> | Booking page title | | Muted | <div> | Secondary text (meta, loading, empty states) | | Error | <div> | Error messages | | Button | <button> | Primary, secondary, and slot buttons | | Input | <input> | Text / email / date inputs | | Textarea | <textarea> | Multi-line responses | | Label | <label> | Form labels | | MarkdownRenderer | lazy-loaded react-markdown + remark-gfm | Renders bookingPage.description (see above) |