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

@vocdoni/react-components

v1.1.0-beta.0

Published

Vocdoni headless react components

Readme

@vocdoni/react-components

Headless, framework-agnostic React components for Vocdoni UIs.

This package keeps business logic and component APIs while delegating visual rendering to overridable slots through ComponentsProvider.

Table of Contents

Getting Started

Install package and peer dependencies:

pnpm add @vocdoni/react-components @vocdoni/sdk react react-i18next i18next date-fns

Pagination APIs are exported from explicit subpaths:

  • @vocdoni/react-components/pagination

If you use routed helpers (for example RoutedPagination), install react-router-dom in your app where routing is configured.

Set providers at app root:

import { ClientProvider, ComponentsProvider } from '@vocdoni/react-components'

export const App = () => (
  <ComponentsProvider>
    <ClientProvider env='prod'>{/* app */}</ClientProvider>
  </ComponentsProvider>
)

How To Use Components

Users consume exported components normally by importing and rendering them:

import { Election, OrganizationName, Balance, Pagination } from '@vocdoni/react-components'

export const Screen = () => (
  <>
    <Balance />
    <OrganizationName />
    <Election electionId='0x...' />
    <Pagination pagination={{ currentPage: 1, previousPage: 0, nextPage: 2, lastPage: 10, totalItems: 200 }} />
  </>
)

ComponentsProvider must wrap ClientProvider so confirmation flows can resolve slot components. If you do not pass custom slots, built-in defaults are used.

Pagination

Pagination is intentionally split into explicit subpaths:

  • @vocdoni/react-components: router-free pagination
  • @vocdoni/react-components/pagination: full pagination API (router-free + route-synced helpers)

Router-Free Pagination

Use this when pagination state lives in React state (no URL sync):

import { PaginationProvider, Pagination } from '@vocdoni/react-components'

export const Members = ({ pagination }) => (
  <PaginationProvider initialPage={1} pagination={pagination}>
    <Pagination pagination={pagination} />
  </PaginationProvider>
)

Routed Pagination

Use this when page changes should update the URL.

Required dependency in your app:

pnpm add react-router-dom

Example:

import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { RoutedPaginationProvider, RoutedPagination } from '@vocdoni/react-components/pagination'

const ProcessList = ({ pagination }) => (
  <RoutedPaginationProvider path='/processes/:page' initialPage={1} pagination={pagination}>
    <RoutedPagination pagination={pagination} />
  </RoutedPaginationProvider>
)

export const App = ({ pagination }) => (
  <BrowserRouter>
    <Routes>
      <Route path='/processes/:page' element={<ProcessList pagination={pagination} />} />
    </Routes>
  </BrowserRouter>
)

Notes:

  • The route must include a :page param if you use RoutedPaginationProvider.
  • Use initialPage consistently with your backend pagination convention (0-based or 1-based).
  • If you only use @vocdoni/react-components/pagination, you do not need react-router-dom.

Default HTML Rendering

@vocdoni/react-components does not include a markdown parser dependency.

By default, description-like fields are rendered as trusted HTML (dangerouslySetInnerHTML). If your app needs markdown parsing or sanitization, override the corresponding slots in ComponentsProvider.

How Customization Works

The canonical customization API is a flat object:

import type { ComponentsDefinition } from '@vocdoni/react-components'

const components: Partial<ComponentsDefinition> = {
  ElectionTitle: ({ title, ...props }) => <h1 {...props}>{title}</h1>,
  PaginationButton: ({ children, ...props }) => <button className='my-btn' {...props}>{children}</button>,
}

Then provide it:

<ComponentsProvider components={components}>{/* app */}</ComponentsProvider>

Confirm Customization

Confirmation modals are fully slot-driven:

  • ConfirmShell: modal wrapper/shell (open state and close action)
  • QuestionsConfirmation: vote confirmation content

Example:

import { ComponentsProvider, composeComponents, defineComponent, type ComponentsPartialDefinition } from '@vocdoni/react-components'

const components: ComponentsPartialDefinition = composeComponents({
  ConfirmShell: defineComponent<'ConfirmShell'>(({ isOpen, onClose, content }) =>
    isOpen ? (
      <div role='dialog'>
        <button onClick={onClose}>Close</button>
        {content}
      </div>
    ) : null
  ),
  QuestionsConfirmation: defineComponent<'QuestionsConfirmation'>(({ answersView, onConfirm, onCancel }) => (
    <div>
      {answersView.map((item) => (
        <div key={item.question}>
          <strong>{item.question}</strong>: {item.answers.join(', ')}
        </div>
      ))}
      <button onClick={onCancel}>Cancel</button>
      <button onClick={onConfirm}>Confirm</button>
    </div>
  )),
})

export const App = () => (
  <ComponentsProvider components={components}>
    <ClientProvider env='prod'>{/* app */}</ClientProvider>
  </ComponentsProvider>
)

The package also exports optional domain helper types and a merge helper for DX:

  • ElectionComponentsDefinition
  • OrganizationComponentsDefinition
  • PaginationComponentsDefinition
  • AccountComponentsDefinition
  • composeComponents(...partials)
import {
  composeComponents,
  type ElectionComponentsDefinition,
  type PaginationComponentsDefinition,
} from '@vocdoni/react-components'

const electionComponents: Partial<ElectionComponentsDefinition> = {
  ElectionTitle: ({ title }) => <h1>{title}</h1>,
}

const paginationComponents: Partial<PaginationComponentsDefinition> = {
  PaginationSummary: ({ text }) => <p>{text}</p>,
}

const components = composeComponents(electionComponents, paginationComponents)

No nested components.election.* API is used.

TypeScript

For strongly typed slot overrides with UI-library props, use defineComponent with a slot key and your extra props:

import { defineComponent, type ComponentsPartialDefinition } from '@vocdoni/react-components'
import type { HeadingProps } from '@chakra-ui/react'

const components: ComponentsPartialDefinition = {
  ElectionTitle: defineComponent<'ElectionTitle', HeadingProps>(({ title, ...props }) => (
    <h1 {...props}>{title}</h1>
  )),
}

This keeps both slot props (for example title, description, label) and your UI props strongly typed.

i18n (i18next)

This package exports i18n resources via the react-components namespace:

  • reactComponentsNamespace
  • reactComponentsDefaultLanguage
  • reactComponentsResources

Example setup:

import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import {
  reactComponentsNamespace,
  reactComponentsResources,
} from '@vocdoni/react-components'

i18n.use(initReactI18next).init({
  lng: 'en',
  resources: reactComponentsResources,
  ns: [reactComponentsNamespace],
  defaultNS: reactComponentsNamespace,
})

Base English template:

src/i18n/locales/en/react-components.json

Testing

From package directory:

pnpm lint
pnpm test
pnpm build

Reference

https://developer.vocdoni.io/ui-components

License

This repository is licensed under the GNU Affero General Public License v3.0.