Tell HeroUI Pro how they could make the product more useful to you.
Have something to say?
RichTextEditor.LinkPopover.Trigger renders an unnecessary nested popover trigger
In @heroui-pro/[email protected], RichTextEditor.LinkPopover.Trigger wraps its existing trigger button in an additional Popover.Trigger. First observed in beta.8; still reproducible in beta.10. beta.9 was not tested separately. Reproduction: Mount the documented RichTextEditor.LinkPopover composition with its Trigger, Content, and Input. Actual: The resulting DOM contains the editor’s link-trigger button plus an additional outer popover-trigger wrapper. Expected: One trigger owns the button’s interaction and popover behavior, without a redundant nested trigger. Local workaround: Remove the outer Popover.Trigger wrapper while preserving the existing button and optional tooltip. Our regression test verifies that the link button remains present and the redundant wrapper is absent. Environment: HeroUI Pro beta.10, HeroUI OSS 3.2.6, React 19.2.8.
RichTextEditor registers duplicate Link and Underline extensions
In @heroui-pro/[email protected], RichTextEditor registers Link and Underline twice: once through Tiptap StarterKit and again as explicitly configured extensions. First observed in beta.8; still reproducible in beta.10. Beta.9 was not tested separately. Reproduction: Mount RichTextEditor with its default extensions. The issue occurs both with and without the optional Markdown extension. Actual: Tiptap reports duplicate extension names. Inspecting editor.extensionManager.extensions shows multiple link and underline entries. Expected: Each extension is registered exactly once. Verified workaround: Disable StarterKit’s copies using link: false and underline: false, retaining Pro’s explicitly configured extensions. Our unchanged regression tests fail against the unpatched package and pass with this correction. Environment: HeroUI Pro beta.10, HeroUI OSS 3.2.6, React 19.2.8.
NumberField: Runaway onChange loop when spam-tapping increment/decrement buttons (iOS)
# NumberField: Runaway onChange loop when spam-tapping increment/decrement buttons (iOS) **Category:** Bug **Severity:** High **Component:** `NumberField` (heroui-native-pro) --- ## Description When using `NumberField` in controlled mode (`value` + `onChange`), rapidly tapping the increment (+) or decrement (-) button on iOS causes `onChange` to fire in an infinite loop. The value runs away to `maxValue` or `minValue` and does not stop even after tapping stops. This is also reproducible in the official **HeroUI Native iOS app**. ## Environment - heroui-native-pro: 1.0.0-beta.4 (also reproduced on beta.3) - heroui-native: ^1.0.3 - expo: ~55.0.26 - react-native: 0.83 - Platform: iOS ## Steps to Reproduce 1. Render a `NumberField` in controlled mode with `value`, `onChange`, `step={50}`, `minValue={0}`, `maxValue={10000}` 2. On iOS, rapidly tap (spam-press) the increment (+) or decrement (-) button 3. Observe the value — it keeps incrementing/decrementing on its own and does not stop ## Expected Behavior Each tap increments/decrements by `step` once. Rapid taps should increment once per tap. The value should stop changing when tapping stops. ## Actual Behavior After rapid taps, `onChange` continues firing in a loop. The value runs away to `maxValue`/`minValue` without stopping. ## Root Cause (from source inspection) Two issues in `useLongPressRepeat`: ### 1. Timer leak on rapid press-in `onPressIn` sets `timerRef.current` to a new `setTimeout` **without clearing the previous one**. During spam-tapping, if two `onPressIn` events fire before `onPressOut` clears them (iOS touch coalescing), the first timeout is leaked — its ref was overwritten, so `clear()` can never cancel it. That orphaned timeout spawns a `setInterval` that runs indefinitely. ```js // useLongPressRepeat.js — current code const onPressIn = useCallback(() => { actionRef.current(); timerRef.current = setTimeout(() => { // ← overwrites without clearing intervalRef.current = setInterval(() => { // ← also overwrites without clearing actionRef.current(); }, interval); }, delay); }, [delay, interval]); ``` ### 2. Stale closure in increment/decrement The `increment`/`decrement` callbacks in `NumberFieldRoot` close over `numberValue` via `useCallback`. When the orphaned interval fires, `actionRef.current` may hold a closure with a stale `numberValue`, causing each tick to re-compute the same step and emit duplicate `onChange` calls. ## Suggested Fix In `useLongPressRepeat`, clear existing timers before starting new ones: ```js const onPressIn = useCallback(() => { if (isDisabledRef.current) return; clear(); // ← clear any existing timer/interval first actionRef.current(); timerRef.current = setTimeout(() => { intervalRef.current = setInterval(() => { actionRef.current(); }, interval); }, delay); }, [delay, interval, clear]); ``` Additionally, consider using a ref for `numberValue` inside `increment`/`decrement` instead of closing over it, so interval ticks always read the latest value. ## Minimal Repro ```tsx import { useCallback, useMemo, useState } from "react"; import { View } from "react-native"; import { Text } from "heroui-native"; import { NumberField } from "heroui-native-pro"; export default function NumberFieldRepro() { const [amount, setAmount] = useState(0); const [changeCount, setChangeCount] = useState(0); const formatOptions = useMemo ( () => ({ style: "currency", currency: "USD", minimumFractionDigits: 0, maximumFractionDigits: 0 }), [], ); const handleChange = useCallback((newValue: number) => { if (isNaN(newValue)) return; console.log(`[NumberField onChange] newValue=${newValue}`); setChangeCount((c) => c + 1); setAmount(newValue); }, []); return (
The theme sync functionality in the theme builder for heroui pro is not working properly
And the difference between the normal theme builder seen in the heroui docs is that the hero ui theme builder gets every single css variable: /* HeroUI Theme Customization Add this to your global.css after importing @heroui/styles Only includes variables users need to customize @see https://heroui.com/docs/react/getting-started/theming */ :root, .light, .default, [data-theme="light"], [data-theme="default"] { /* Theme Colors (Light Mode) */ --accent: oklch(0 0 0); --accent-foreground: oklch(99.11% 0 0); --background: oklch(97.02% 0.0000 0.00); --border: oklch(90.00% 0.0000 0.00); --danger: oklch(0.573 0.2249 21.97); --danger-foreground: oklch(98% 0.0200 21.97); --default: oklch(94.00% 0.0000 0.00); --default-foreground: oklch(21.03% 0.0059 0.00); --field-background: oklch(100.00% 0.0000 0.00); --field-border: transparent; --field-foreground: oklch(21.03% 0.0000 0.00); --field-placeholder: oklch(55.17% 0.0000 0.00); --focus: oklch(0 0 0); --foreground: oklch(21.03% 0.0000 0.00); --muted: oklch(55.17% 0.0000 0.00); --overlay: oklch(100.00% 0.0000 0.00); --overlay-foreground: oklch(21.03% 0.0000 0.00); --scrollbar: oklch(87.10% 0.0000 0.00); --segment: oklch(100.00% 0.0000 0.00); --segment-foreground: oklch(21.03% 0.0000 0.00); --separator: oklch(92.00% 0.0000 0.00); --success: oklch(0.6277 0.1604 153.06); --success-foreground: oklch(98% 0.0160 153.06); --surface: oklch(100.00% 0.0000 0.00); --surface-foreground: oklch(21.03% 0.0000 0.00); --surface-secondary: oklch(95.24% 0.0000 0.00); --surface-secondary-foreground: oklch(21.03% 0.0000 0.00); --surface-tertiary: oklch(93.73% 0.0000 0.00); --surface-tertiary-foreground: oklch(21.03% 0.0000 0.00); --warning: oklch(0.8446 0.1525 80.6); --warning-foreground: oklch(15% 0.0457 80.60); --accent-soft-foreground: oklch(0 0 0); /* Border Radius */ --radius: 0.25rem; --field-radius: 0.25rem; /* Font Family / / Make sure to load Inter font in your app */ --font-sans: var(--font-inter); } .dark, [data-theme="dark"] { color-scheme: dark; /* Theme Colors (Dark Mode) */ --accent: oklch(0.9848 0 0); --accent-foreground: oklch(15% 0.0000 0.00); --background: oklch(12.00% 0.0000 0.00); --border: oklch(28.00% 0.0000 0.00); --danger: oklch(0.7044 0.1872 23.19); --danger-foreground: oklch(15% 0.0500 23.19); --default: oklch(27.40% 0.0000 0.00); --default-foreground: oklch(99.11% 0 0); --field-background: oklch(21.03% 0.0000 0.00); --field-border: transparent; --field-foreground: oklch(99.11% 0.0000 0.00); --field-placeholder: oklch(70.50% 0.0000 0.00); --focus: oklch(0.9848 0 0); --foreground: oklch(99.11% 0.0000 0.00); --muted: oklch(70.50% 0.0000 0.00); --overlay: oklch(21.03% 0.0000 0.00); --overlay-foreground: oklch(99.11% 0.0000 0.00); --scrollbar: oklch(70.50% 0.0000 0.00); --segment: oklch(39.64% 0.0000 0.00); --segment-foreground: oklch(99.11% 0.0000 0.00); --separator: oklch(25.00% 0.0000 0.00); --success: oklch(0.6514 0.1321 156.22); --success-foreground: oklch(15% 0.0396 156.22); --surface: oklch(21.03% 0.0000 0.00); --surface-foreground: oklch(99.11% 0.0000 0.00); --surface-secondary: oklch(25.70% 0.0000 0.00); --surface-secondary-foreground: oklch(99.11% 0.0000 0.00); --surface-tertiary: oklch(27.21% 0.0000 0.00); --surface-tertiary-foreground: oklch(99.11% 0.0000 0.00); --warning: oklch(0.8803 0.1348 86.06); --warning-foreground: oklch(15% 0.0404 86.06); --accent-soft-foreground: oklch(0.9848 0 0); } This is the pro version: /* HeroUI Pro Theme Add this to your globals.css after importing @heroui/styles @see https://heroui.com/docs/react/getting-started/theming */ /* Font: Inter Load this font in your app: */ @import "@heroui-pro
Error with multiple DataGrid on a page
The DataGrid work fine if there’s only 1 on a page. A second DataGrid causes the following error. I’ve tried both grids independently, and extracted to a common component. Tried both beta 2 and 3. error-boundary-callbacks.ts:90 Error: A table must have at least one Column with the isRowHeader prop set to true at $76d00c5a4edb230a$var$TableCollection.updateColumns (Table.tsx:174:13) at $76d00c5a4edb230a$var$TableCollection.commit (Table.tsx:117:10) at $96ead35620b8fd36$export$b34a105447964f9f.updateCollection (Document.ts:521:27) at $96ead35620b8fd36$export$b34a105447964f9f.getCollection (Document.ts:487:10) at CollectionBuilder.tsx:102:31 at updateSyncExternalStore (react-dom-client.development.js:8499:31) at Object.useSyncExternalStore (react-dom-client.development.js:28671:16) at exports.useSyncExternalStore (react.development.js:1316:34) at $42ceafc619f9c3ba$var$useCollectionDocument (CollectionBuilder.tsx:115:20) at $42ceafc619f9c3ba$export$bf788dd355e3a401 (CollectionBuilder.tsx:53:32) at Object.react_stack_bottom_frame (react-dom-client.development.js:28038:20) at renderWithHooks (react-dom-client.development.js:7984:22) at updateFunctionComponent (react-dom-client.development.js:10501:19) at beginWork (react-dom-client.development.js:12136:18) at runWithFiberInDEV (react-dom-client.development.js:986:30) at performUnitOfWork (react-dom-client.development.js:18997:22) at workLoopSync (react-dom-client.development.js:18825:41) at renderRootSync (react-dom-client.development.js:18806:11) at performWorkOnRoot (react-dom-client.development.js:17914:35) at performSyncWorkOnRoot (react-dom-client.development.js:20399:7) at flushSyncWorkAcrossRoots_impl (react-dom-client.development.js:20241:21) at flushSpawnedWork (react-dom-client.development.js:19752:9) at commitRoot (react-dom-client.development.js:19335:60) at commitRootWhenReady (react-dom-client.development.js:18178:7) at performWorkOnRoot (react-dom-client.development.js:18054:15) at performWorkOnRootViaSchedulerTask (react-dom-client.development.js:20384:7) at MessagePort.performWorkUntilDeadline (scheduler.development.js:45:48) The above error occurred in the component. It was handled by the error boundary. 'use client'; import { DataGrid, EmptyState } from '@heroui-pro/react'; import { SortableLibraryItem } from './sorting'; interface ItemTableProps { title?: string; items: (SortableLibraryItem & { _id: string })[]; tableKey: string; } export function ItemTable({ title, items, tableKey }: ItemTableProps) { return ( ( {title} Title / Key ), minWidth: 200, // accessorKey: 'title', cell: (item) => ( {item.title} {item.key} ), }, { id: 'type', headerClassName: 'content-end align-end', header: 'Type', accessorKey: 'subType', // minWidth: 120, maxWidth: 200, }, { id: 'storyDate', header: 'Story Date', headerClassName: 'content-end align-end', // minWidth: 120, maxWidth: 200, cell: (item) => (
beta 2 import bugs
After update to beta 2, it always show this error on all heroui-native-pro imports no matter which component is imported [TypeError: Cannot read property 'Bar' of undefined] It never occurs in beta 1, maybe problems with Bar Chart exports. Please fix that, Thank you.