
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@vocdoni/react-components
Advanced tools
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.
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/paginationIf 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>
)
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 is intentionally split into explicit subpaths:
@vocdoni/react-components: router-free pagination@vocdoni/react-components/pagination: full pagination API (router-free + route-synced helpers)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>
)
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:
:page param if you use RoutedPaginationProvider.initialPage consistently with your backend pagination convention (0-based or 1-based).@vocdoni/react-components/pagination, you do not need react-router-dom.@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.
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>
Confirmation modals are fully slot-driven:
ConfirmShell: modal wrapper/shell (open state and close action)QuestionsConfirmation: vote confirmation contentExample:
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:
ElectionComponentsDefinitionOrganizationComponentsDefinitionPaginationComponentsDefinitionAccountComponentsDefinitioncomposeComponents(...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.
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.
This package exports i18n resources via the react-components namespace:
reactComponentsNamespacereactComponentsDefaultLanguagereactComponentsResourcesExample 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
From package directory:
pnpm lint
pnpm test
pnpm build
https://developer.vocdoni.io/ui-components
This repository is licensed under the GNU Affero General Public License v3.0.
FAQs
Vocdoni headless react components
We found that @vocdoni/react-components demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.