diff --git a/examples/button/button.polymorph.tsx b/examples/button/button.polymorph.tsx
index 3609ffff8..ea99ef348 100644
--- a/examples/button/button.polymorph.tsx
+++ b/examples/button/button.polymorph.tsx
@@ -46,7 +46,6 @@ function Component() {
- {/* @ts-expect-error TODO: fix `as` inference */}
diff --git a/examples/dropdown/dropdown.customItem.tsx b/examples/dropdown/dropdown.customItem.tsx
index 96509097b..d6fbc7aa0 100644
--- a/examples/dropdown/dropdown.customItem.tsx
+++ b/examples/dropdown/dropdown.customItem.tsx
@@ -41,7 +41,6 @@ function Component() {
function Component() {
return (
- {/* @ts-expect-error TODO: fix `as` inference */}
Home
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
index 1df740f29..53c672d12 100644
--- a/src/components/Button/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -1,7 +1,6 @@
-import type { ComponentPropsWithoutRef, ElementType, ForwardedRef } from 'react';
-import { type ReactNode } from 'react';
+import type { ElementType } from 'react';
+import { forwardRef, type ReactNode } from 'react';
import { twMerge } from 'tailwind-merge';
-import genericForwardRef from '../../helpers/generic-forward-ref';
import { mergeDeep } from '../../helpers/merge-deep';
import { getTheme } from '../../theme-store';
import type { DeepPartial } from '../../types';
@@ -16,6 +15,7 @@ import { Spinner } from '../Spinner';
import { ButtonBase, type ButtonBaseProps } from './ButtonBase';
import type { PositionInButtonGroup } from './ButtonGroup';
import { ButtonGroup } from './ButtonGroup';
+import type { PolymorphicComponentPropWithRef, PolymorphicRef } from '../../helpers/generic-as-prop';
export interface FlowbiteButtonTheme {
base: string;
@@ -67,105 +67,110 @@ export interface ButtonSizes extends Pick = {
- as?: T | null;
- href?: string;
- color?: keyof FlowbiteColors;
- fullSized?: boolean;
- gradientDuoTone?: keyof ButtonGradientDuoToneColors;
- gradientMonochrome?: keyof ButtonGradientColors;
- target?: string;
- isProcessing?: boolean;
- processingLabel?: string;
- processingSpinner?: ReactNode;
- label?: ReactNode;
- outline?: boolean;
- pill?: boolean;
- positionInGroup?: keyof PositionInButtonGroup;
- size?: keyof ButtonSizes;
- theme?: DeepPartial;
-} & ComponentPropsWithoutRef;
-
-const ButtonComponentFn = (
+export type ButtonProps = PolymorphicComponentPropWithRef<
+ T,
{
- children,
- className,
- color = 'info',
- disabled,
- fullSized,
- isProcessing = false,
- processingLabel = 'Loading...',
- processingSpinner,
- gradientDuoTone,
- gradientMonochrome,
- label,
- outline = false,
- pill = false,
- positionInGroup = 'none',
- size = 'md',
- theme: customTheme = {},
- ...props
- }: ButtonProps,
- ref: ForwardedRef,
-) => {
- const { buttonGroup: groupTheme, button: buttonTheme } = getTheme();
- const theme = mergeDeep(buttonTheme, customTheme);
+ href?: string;
+ color?: keyof FlowbiteColors;
+ fullSized?: boolean;
+ gradientDuoTone?: keyof ButtonGradientDuoToneColors;
+ gradientMonochrome?: keyof ButtonGradientColors;
+ target?: string;
+ isProcessing?: boolean;
+ processingLabel?: string;
+ processingSpinner?: ReactNode;
+ label?: ReactNode;
+ outline?: boolean;
+ pill?: boolean;
+ positionInGroup?: keyof PositionInButtonGroup;
+ size?: keyof ButtonSizes;
+ theme?: DeepPartial;
+ }
+>;
+
+type ButtonComponentType = ((
+ props: ButtonProps,
+) => React.ReactNode | null) & { displayName?: string };
+
+const ButtonComponentFn: ButtonComponentType = forwardRef(
+ (
+ {
+ children,
+ className,
+ color = 'info',
+ disabled,
+ fullSized,
+ isProcessing = false,
+ processingLabel = 'Loading...',
+ processingSpinner,
+ gradientDuoTone,
+ gradientMonochrome,
+ label,
+ outline = false,
+ pill = false,
+ positionInGroup = 'none',
+ size = 'md',
+ theme: customTheme = {},
+ ...props
+ }: ButtonProps,
+ ref: PolymorphicRef,
+ ) => {
+ const { buttonGroup: groupTheme, button: buttonTheme } = getTheme();
+ const theme = mergeDeep(buttonTheme, customTheme);
- const theirProps = props as ButtonBaseProps;
+ const theirProps = props as ButtonBaseProps;
- return (
-
-
- <>
- {isProcessing && (
-
- {processingSpinner || }
-
+
- {isProcessing ? processingLabel : label}
-
- )}
- >
-
-
- );
-};
+ >
+ <>
+ {isProcessing && (
+
+ {processingSpinner || }
+
+ )}
+ {typeof children !== 'undefined' ? (
+ children
+ ) : (
+
+ {isProcessing ? processingLabel : label}
+
+ )}
+ >
+
+
+ );
+ },
+);
ButtonComponentFn.displayName = 'Button';
-
-const ButtonComponent = genericForwardRef(ButtonComponentFn);
-
-export const Button = Object.assign(ButtonComponent, {
+export const Button = Object.assign(ButtonComponentFn, {
Group: ButtonGroup,
});
diff --git a/src/components/Button/ButtonBase.tsx b/src/components/Button/ButtonBase.tsx
index 42bd13799..6bb5a97c1 100644
--- a/src/components/Button/ButtonBase.tsx
+++ b/src/components/Button/ButtonBase.tsx
@@ -1,18 +1,19 @@
-import { createElement, type ComponentPropsWithoutRef, type ElementType, type ForwardedRef } from 'react';
-import genericForwardRef from '../../helpers/generic-forward-ref';
+import { createElement, type ComponentPropsWithoutRef, type ElementType, type ForwardedRef, forwardRef } from 'react';
export type ButtonBaseProps = {
as?: T;
href?: string;
} & ComponentPropsWithoutRef;
-const ButtonBaseComponent = (
- { children, as: Component, href, type = 'button', ...props }: ButtonBaseProps,
- ref: ForwardedRef,
-) => {
- const BaseComponent = Component || (href ? 'a' : 'button');
+export const ButtonBase = forwardRef(
+ (
+ { children, as: Component, href, type = 'button', ...props }: ButtonBaseProps,
+ ref: ForwardedRef,
+ ) => {
+ const BaseComponent = Component || (href ? 'a' : 'button');
- return createElement(BaseComponent, { ref, href, type, ...props }, children);
-};
+ return createElement(BaseComponent, { ref, href, type, ...props }, children);
+ },
+);
-export const ButtonBase = genericForwardRef(ButtonBaseComponent);
+ButtonBase.displayName = 'ButtonBaseComponent';
diff --git a/src/components/Dropdown/Dropdown.spec.tsx b/src/components/Dropdown/Dropdown.spec.tsx
index c5f1895c8..fec8846ae 100644
--- a/src/components/Dropdown/Dropdown.spec.tsx
+++ b/src/components/Dropdown/Dropdown.spec.tsx
@@ -167,7 +167,7 @@ describe('Components / Dropdown', () => {
});
describe('Dropdown item render', async () => {
- it('should override Dropdownn.Item base component when using `as` prop', async () => {
+ it('should override Dropdown.Item base component when using `as` prop', async () => {
const user = userEvent.setup();
const CustomBaseItem = ({ children }: PropsWithChildren) => {
diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx
index 8398785b1..3b34ac8f1 100644
--- a/src/components/Dropdown/Dropdown.tsx
+++ b/src/components/Dropdown/Dropdown.tsx
@@ -10,7 +10,6 @@ import type {
MutableRefObject,
ReactElement,
ReactNode,
- RefCallback,
SetStateAction,
} from 'react';
import { cloneElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -98,13 +97,7 @@ const Trigger = ({
{children}
) : (
-