diff --git a/.changeset/poor-sheep-repair.md b/.changeset/poor-sheep-repair.md
new file mode 100644
index 0000000000..fa819c548c
--- /dev/null
+++ b/.changeset/poor-sheep-repair.md
@@ -0,0 +1,6 @@
+---
+"@nextui-org/alert": minor
+"@nextui-org/theme": minor
+---
+
+introduced Alert component (#2250)
diff --git a/apps/docs/config/routes.json b/apps/docs/config/routes.json
index 583e220fa7..b38e7c8854 100644
--- a/apps/docs/config/routes.json
+++ b/apps/docs/config/routes.json
@@ -144,6 +144,12 @@
"keywords": "autocomplete, auto suggest, search, typeahead",
"path": "/docs/components/autocomplete.mdx"
},
+ {
+ "key": "alert",
+ "title": "Alert",
+ "keywords": "alert, notification, message",
+ "path": "/docs/components/alert.mdx"
+ },
{
"key": "avatar",
"title": "Avatar",
diff --git a/apps/docs/content/components/alert/colors.ts b/apps/docs/content/components/alert/colors.ts
new file mode 100644
index 0000000000..c4877551ec
--- /dev/null
+++ b/apps/docs/content/components/alert/colors.ts
@@ -0,0 +1,27 @@
+const App = `import {Alert} from "@nextui-org/react";
+
+export default function App() {
+ const title = "Email Sent!!";
+ const description = "You will get a reply soon";
+
+ return (
+
+
+ {["default", "primary", "secondary", "success", "warning", "danger"].map((color) => (
+
+ ))}
+
+
+ );
+}`;
+
+const react = {
+ "/App.jsx": App,
+};
+
+export default {
+ ...react,
+};
diff --git a/apps/docs/content/components/alert/custom-impl.ts b/apps/docs/content/components/alert/custom-impl.ts
new file mode 100644
index 0000000000..9f5a2c70a2
--- /dev/null
+++ b/apps/docs/content/components/alert/custom-impl.ts
@@ -0,0 +1,142 @@
+const InfoCircleIcon = `export const InfoCircleIcon = (props) => (
+
+);`;
+
+const CloseIcon = `export const CloseIcon = (props) => (
+
+);`;
+
+const App = `import React, {forwardRef, useMemo} from "react";
+import {useAlert} from "@nextui-org/react";
+import {InfoCircleIcon} from "./InfoCircleIcon";
+import {CloseIcon} from "./CloseIcon"
+
+const styles = {
+ base: [
+ "bg-slate-100",
+ "border",
+ "shadow",
+ "hover:bg-slate-200",
+ "focus-within:!bg-slate-100",
+ "dark:bg-slate-900",
+ "dark:hover:bg-slate-800",
+ "dark:border-slate-800",
+ "dark:focus-within:!bg-slate-900",
+ "cursor-pointer"
+ ],
+ title: [
+ "text-base",
+ "text-slate-500",
+ "font-bold"
+ ],
+ description: [
+ "text-base",
+ "text-slate-500",
+ ],
+}
+
+const MyAlert = forwardRef((props, ref) => {
+ const {
+ title,
+ description,
+ isClosable,
+ domRef,
+ handleClose,
+ getBaseProps,
+ getMainWrapperProps,
+ getDescriptionProps,
+ getTitleProps,
+ getCloseButtonProps,
+ color,
+ isVisible,
+ onClose,
+ getCloseIconProps,
+ getAlertIconProps,
+ } = useAlert({
+ ...props,
+ ref,
+ // this is just for the example, the props bellow should be passed by the parent component
+ title: "Email Sent!!",
+ description: "You will get a reply soon",
+ // custom styles
+ classNames: {
+ ...styles,
+ },
+ });
+
+ const mainWrapper = useMemo(() => {
+ return (
+
+ {title &&
{title}
}
+
{description}
+
+ );
+ }, [title, description, getMainWrapperProps, getTitleProps, getDescriptionProps]);
+
+ const baseWrapper = useMemo(() => {
+ return isVisible ? (
+
+
+ {mainWrapper}
+ {(isClosable || onClose) && (
+
+ )}
+
+ ) : null;
+ }, [
+ mainWrapper,
+ isClosable,
+ getCloseButtonProps,
+ isVisible,
+ domRef,
+ getBaseProps,
+ handleClose,
+ color,
+ onClose,
+ getAlertIconProps,
+ ]);
+
+ return <>{baseWrapper}>;
+});
+
+MyAlert.displayName = "MyAlert";
+
+export default MyAlert;`;
+
+const react = {
+ "/App.jsx": App,
+ "/InfoCircleIcon": InfoCircleIcon,
+ "/CloseIcon": CloseIcon,
+};
+
+export default {
+ ...react,
+};
diff --git a/apps/docs/content/components/alert/custom-styles.ts b/apps/docs/content/components/alert/custom-styles.ts
new file mode 100644
index 0000000000..dc9f6e3deb
--- /dev/null
+++ b/apps/docs/content/components/alert/custom-styles.ts
@@ -0,0 +1,46 @@
+const App = `import {Alert} from "@nextui-org/react";
+
+export default function App() {
+ const title = "Email Sent!!";
+ const description = "You will get a reply soon";
+
+ return (
+
+ );
+}`;
+
+const react = {
+ "/App.jsx": App,
+};
+
+export default {
+ ...react,
+};
diff --git a/apps/docs/content/components/alert/index.ts b/apps/docs/content/components/alert/index.ts
new file mode 100644
index 0000000000..e0f9f738c9
--- /dev/null
+++ b/apps/docs/content/components/alert/index.ts
@@ -0,0 +1,15 @@
+import colors from "./colors";
+import usage from "./usage";
+import isClosable from "./is-closable";
+import radius from "./radius";
+import customImpl from "./custom-impl";
+import customStyles from "./custom-styles";
+
+export const alertContent = {
+ colors,
+ usage,
+ isClosable,
+ radius,
+ customImpl,
+ customStyles,
+};
diff --git a/apps/docs/content/components/alert/is-closable.ts b/apps/docs/content/components/alert/is-closable.ts
new file mode 100644
index 0000000000..e39f9b95d8
--- /dev/null
+++ b/apps/docs/content/components/alert/is-closable.ts
@@ -0,0 +1,20 @@
+const App = `import {Alert} from "@nextui-org/react";
+
+export default function App() {
+ const title = "Email Sent!!";
+ const description = "You will get a reply soon";
+
+ return (
+
+ );
+}`;
+
+const react = {
+ "/App.jsx": App,
+};
+
+export default {
+ ...react,
+};
diff --git a/apps/docs/content/components/alert/radius.ts b/apps/docs/content/components/alert/radius.ts
new file mode 100644
index 0000000000..19c3f552e8
--- /dev/null
+++ b/apps/docs/content/components/alert/radius.ts
@@ -0,0 +1,27 @@
+const App = `import {Alert} from "@nextui-org/react";
+
+export default function App() {
+ const title = "Email Sent!!";
+ const description = "You will get a reply soon";
+
+ return (
+
+
+ {["none", "sm", "md", "lg", "full"].map((radius) => (
+
+ ))}
+
+
+ );
+}`;
+
+const react = {
+ "/App.jsx": App,
+};
+
+export default {
+ ...react,
+};
diff --git a/apps/docs/content/components/alert/usage.ts b/apps/docs/content/components/alert/usage.ts
new file mode 100644
index 0000000000..faf2a032a5
--- /dev/null
+++ b/apps/docs/content/components/alert/usage.ts
@@ -0,0 +1,20 @@
+const App = `import {Alert} from "@nextui-org/react";
+
+export default function App() {
+ const title = "Email Sent!!";
+ const description = "You will get a reply soon";
+
+ return (
+
+ );
+}`;
+
+const react = {
+ "/App.jsx": App,
+};
+
+export default {
+ ...react,
+};
diff --git a/apps/docs/content/components/index.ts b/apps/docs/content/components/index.ts
index ac6e664826..2641e4c404 100644
--- a/apps/docs/content/components/index.ts
+++ b/apps/docs/content/components/index.ts
@@ -31,3 +31,4 @@ export * from "./dropdown";
export * from "./navbar";
export * from "./table";
export * from "./autocomplete";
+export * from "./alert";
diff --git a/apps/docs/content/docs/components/alert.mdx b/apps/docs/content/docs/components/alert.mdx
new file mode 100644
index 0000000000..6da1651c1e
--- /dev/null
+++ b/apps/docs/content/docs/components/alert.mdx
@@ -0,0 +1,125 @@
+---
+title: "Alert"
+description: "Alerts are temporary notifications that provide concise feedback about an action or event."
+---
+
+import {alertContent} from "@/content/components/alert";
+
+# Alert
+
+Alerts are temporary notifications that provide concise feedback about an action or event.
+
+
+
+---
+
+
+
+## Installation
+
+
+
+## Import
+
+
+
+
+## Usage
+- It accepts `title` and `description` as props for the alert message.
+- `title` is an optional property.
+
+
+
+### Radius
+
+
+
+### Colors
+
+
+
+
+### isClosable
+
+If `isClosable` is true, a close button appears on the alert, which can be used to close it.
+
+> **Note**: If the `onClose` is passed, the close button is visible regardless of the `isClosable` property.
+
+
+
+
+
+
+## Slots
+
+- **base**:
+ Alert wrapper, it handles alignment, placement, and general appearance.
+- **mainWrapper**:
+ Wraps the `title` and `description` of the alert.
+- **closeButton**:
+ The `closeButton`, it is in the top-right corner of alert.
+- **description**:
+ The description of the alert.
+- **title**:
+ The title of the alert.
+- **closeIcon**:
+ The close icon that is wrapped inside the `closeButton`.
+- **alertIcon**:
+ icon that appears at the top-left corner.
+
+
+
+### Custom Alert Styles
+
+You can customize the alert styles by using the `classNames` property.
+
+Here's an example of how to customize the alert styles:
+
+
+
+
+
+### Custom Implementation
+
+In case you need to customize the alert even further, you can use the `useAlert` hook to create your own implementation.
+
+
+
+
+
+## API
+
+### Alert Props
+
+| Attribute | Type | Description | Default |
+| ---------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------ | --------- |
+| title | `string` | Title for alert | - |
+| description | `ReactNode` | Description for alert message | - |
+| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The alert color theme. | `default` |
+| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The alert border radius. | `md` |
+| isClosable | `boolean` | Whether the close button should be displayed. | `false` |
+
+### Alert Events
+
+| Attribute | Type | Description |
+| ------------ | --------------------------- | ----------------------------------------------------------- |
+| onClose | `() => void` | Handler that is called when the close button is clicked. |
\ No newline at end of file
diff --git a/packages/components/alert/README.md b/packages/components/alert/README.md
new file mode 100644
index 0000000000..071ac6dd26
--- /dev/null
+++ b/packages/components/alert/README.md
@@ -0,0 +1,24 @@
+# @nextui-org/alert
+
+Alerts are temporary notifications that provide concise feedback about an action or event.
+
+Please refer to the [documentation](https://nextui.org/docs/components/alert) for more information.
+
+## Installation
+
+```sh
+yarn add @nextui-org/alert
+# or
+npm i @nextui-org/alert
+```
+
+## Contribution
+
+Yes please! See the
+[contributing guidelines](https://github.com/nextui-org/nextui/blob/master/CONTRIBUTING.md)
+for details.
+
+## License
+
+This project is licensed under the terms of the
+[MIT license](https://github.com/nextui-org/nextui/blob/master/LICENSE).
diff --git a/packages/components/alert/__tests__/alert.test.tsx b/packages/components/alert/__tests__/alert.test.tsx
new file mode 100644
index 0000000000..dfa3074c18
--- /dev/null
+++ b/packages/components/alert/__tests__/alert.test.tsx
@@ -0,0 +1,85 @@
+import * as React from "react";
+import {act, render} from "@testing-library/react";
+
+import {Alert} from "../src";
+
+const title = "Testing Title";
+const description = "Testing Description";
+
+describe("Alert", () => {
+ it("should render correctly", () => {
+ const wrapper = render();
+
+ expect(() => wrapper.unmount()).not.toThrow();
+ });
+
+ it("ref should be forwarded", () => {
+ const ref = React.createRef();
+
+ render();
+
+ expect(ref.current).not.toBeNull();
+ });
+
+ it("should display title and description when component is rendered", () => {
+ const wrapper = render();
+
+ const titleElement = wrapper.getByText(title);
+ const descriptionElement = wrapper.getByText(description);
+
+ expect(titleElement).toContainHTML(title);
+ expect(descriptionElement).toContainHTML(description);
+ });
+
+ it("should show close button when is Closable", () => {
+ const {getByRole} = render();
+
+ const closeButton = getByRole("button");
+
+ expect(closeButton).toBeVisible();
+ });
+
+ it("should show close button when onClose is passed", () => {
+ const onClose = jest.fn();
+
+ const {getByRole} = render();
+
+ const closeButton = getByRole("button");
+
+ expect(closeButton).toBeVisible();
+ });
+
+ it("should not show close button when not isClosable and onClose is not passed", () => {
+ const wrapper = render();
+
+ const closeButton = wrapper.queryByRole("button");
+
+ expect(closeButton).toBeNull();
+ });
+
+ it("should call the onClose function when clicking on close button", () => {
+ const onClose = jest.fn();
+
+ const wrapper = render();
+
+ const closeButton = wrapper.getByRole("button");
+
+ act(() => {
+ closeButton.click();
+ });
+
+ expect(onClose).toHaveBeenCalled();
+ });
+
+ it("should close the alert when clicking on close button", () => {
+ const wrapper = render();
+
+ const closeButton = wrapper.getByRole("button");
+
+ act(() => {
+ closeButton.click();
+ });
+
+ expect(wrapper.container).toBeEmptyDOMElement();
+ });
+});
diff --git a/packages/components/alert/package.json b/packages/components/alert/package.json
new file mode 100644
index 0000000000..d06a00d598
--- /dev/null
+++ b/packages/components/alert/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "@nextui-org/alert",
+ "version": "2.0.0",
+ "description": "Alerts are temporary notifications that provide concise feedback about an action or event.",
+ "keywords": [
+ "alert"
+ ],
+ "author": "Junior Garcia ",
+ "contributors": [
+ "Abhinav Agarwal ",
+ "WK Wong "
+ ],
+ "homepage": "https://nextui.org",
+ "license": "MIT",
+ "main": "src/index.ts",
+ "sideEffects": false,
+ "files": [
+ "dist"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/nextui-org/nextui.git",
+ "directory": "packages/components/alert"
+ },
+ "bugs": {
+ "url": "https://github.com/nextui-org/nextui/issues"
+ },
+ "scripts": {
+ "build": "tsup src --dts",
+ "dev": "pnpm build:fast --watch",
+ "clean": "rimraf dist .turbo",
+ "typecheck": "tsc --noEmit",
+ "build:fast": "tsup src",
+ "prepack": "clean-package",
+ "postpack": "clean-package restore"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18",
+ "@nextui-org/theme": ">=2.1.0",
+ "@nextui-org/system": ">=2.0.0"
+ },
+ "dependencies": {
+ "@nextui-org/react-utils": "workspace:*",
+ "@nextui-org/shared-icons": "workspace:*",
+ "@nextui-org/shared-utils": "workspace:*"
+ },
+ "devDependencies": {
+ "@nextui-org/system": "workspace:*",
+ "@nextui-org/theme": "workspace:*",
+ "clean-package": "2.2.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "clean-package": "../../../clean-package.config.json"
+ }
\ No newline at end of file
diff --git a/packages/components/alert/src/alert.tsx b/packages/components/alert/src/alert.tsx
new file mode 100644
index 0000000000..e97880144d
--- /dev/null
+++ b/packages/components/alert/src/alert.tsx
@@ -0,0 +1,85 @@
+import {useMemo} from "react";
+import {forwardRef} from "@nextui-org/system";
+import {
+ CloseIcon,
+ DangerIcon,
+ InfoCircleIcon,
+ SuccessIcon,
+ WarningIcon,
+} from "@nextui-org/shared-icons";
+
+import {useAlert, UseAlertProps} from "./use-alert";
+
+export interface AlertProps extends UseAlertProps {}
+
+const Alert = forwardRef<"div", AlertProps>((props, ref) => {
+ const {
+ title,
+ description,
+ isClosable,
+ domRef,
+ handleClose,
+ getBaseProps,
+ getMainWrapperProps,
+ getDescriptionProps,
+ getTitleProps,
+ getCloseButtonProps,
+ color,
+ isVisible,
+ onClose,
+ getAlertIconProps,
+ } = useAlert({...props, ref});
+
+ const mainWrapper = useMemo(() => {
+ return (
+
+ {title &&
{title}
}
+
{description}
+
+ );
+ }, [title, description, getMainWrapperProps, getTitleProps, getDescriptionProps]);
+
+ const iconMap = {
+ primary: InfoCircleIcon,
+ secondary: InfoCircleIcon,
+ success: SuccessIcon,
+ warning: WarningIcon,
+ danger: DangerIcon,
+ };
+
+ const IconComponent = iconMap[color] || InfoCircleIcon;
+
+ const alertIcon = useMemo(() => {
+ return ;
+ }, [color, getAlertIconProps]);
+
+ const baseWrapper = useMemo(() => {
+ return isVisible ? (
+
+ {alertIcon}
+ {mainWrapper}
+ {(isClosable || onClose) && (
+
+ )}
+
+ ) : null;
+ }, [
+ mainWrapper,
+ isClosable,
+ getCloseButtonProps,
+ isVisible,
+ domRef,
+ getBaseProps,
+ handleClose,
+ onClose,
+ alertIcon,
+ ]);
+
+ return <>{baseWrapper}>;
+});
+
+Alert.displayName = "NextUI.Alert";
+
+export default Alert;
diff --git a/packages/components/alert/src/index.ts b/packages/components/alert/src/index.ts
new file mode 100644
index 0000000000..e1d0ea8cf6
--- /dev/null
+++ b/packages/components/alert/src/index.ts
@@ -0,0 +1,10 @@
+import Alert from "./alert";
+
+// export types
+export type {AlertProps} from "./alert";
+
+// export hooks
+export {useAlert} from "./use-alert";
+
+// export component
+export {Alert};
diff --git a/packages/components/alert/src/use-alert.ts b/packages/components/alert/src/use-alert.ts
new file mode 100644
index 0000000000..ea6026d222
--- /dev/null
+++ b/packages/components/alert/src/use-alert.ts
@@ -0,0 +1,124 @@
+import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
+import {AlertSlots, SlotsToClasses} from "@nextui-org/theme";
+import {ReactRef, useDOMRef} from "@nextui-org/react-utils";
+import {AlertVariantProps} from "@nextui-org/theme/src/components/alert";
+import {ReactNode, useCallback, useMemo} from "react";
+import {alert} from "@nextui-org/theme";
+import {useState} from "react";
+import {objectToDeps} from "@nextui-org/shared-utils";
+
+interface Props extends HTMLNextUIProps<"div"> {
+ /**
+ * Ref to the DOM node.
+ */
+ ref?: ReactRef;
+
+ /**
+ * title of the alert message
+ */
+ title?: string;
+
+ /**
+ * Main body of the alert message
+ */
+ description: ReactNode;
+
+ /**
+ * whether the alert can be closed by user
+ */
+ isClosable?: boolean;
+
+ /**
+ * function which is called when close button is clicked
+ */
+ onClose?: () => void;
+
+ /**
+ * Classname or List of classes to change the classNames of the element.
+ * if `className` is passed, it will be added to the base slot.
+ *
+ * @example
+ * ```ts
+ *
+ * ```
+ */
+ classNames?: SlotsToClasses;
+}
+
+export type UseAlertProps = Props & AlertVariantProps;
+
+export function useAlert(originalProps: UseAlertProps) {
+ const [props, variantProps] = mapPropsVariants(originalProps, alert.variantKeys);
+
+ const {title, description, onClose, isClosable, ref, classNames} = props;
+
+ const [isVisible, setIsVisible] = useState(true);
+
+ const handleClose = () => {
+ setIsVisible(false);
+ onClose?.();
+ };
+ const domRef = useDOMRef(ref);
+
+ const slots = useMemo(() => alert({...variantProps}), [objectToDeps(variantProps)]);
+
+ const getBaseProps = useCallback(() => {
+ return {
+ className: slots.base({class: classNames?.base}),
+ };
+ }, [slots, classNames?.base]);
+
+ const getMainWrapperProps = useCallback(() => {
+ return {
+ className: slots.mainWrapper({class: classNames?.mainWrapper}),
+ };
+ }, [slots, classNames?.mainWrapper]);
+
+ const getDescriptionProps = useCallback(() => {
+ return {
+ className: slots.description({class: classNames?.description}),
+ };
+ }, [slots, classNames?.description]);
+
+ const getTitleProps = useCallback(() => {
+ return {
+ className: slots.title({class: classNames?.title}),
+ };
+ }, [slots, classNames?.title]);
+
+ const getCloseButtonProps = useCallback(() => {
+ return {
+ className: slots.closeButton({class: classNames?.closeButton}),
+ };
+ }, [slots, classNames?.closeButton]);
+
+ const getAlertIconProps = useCallback(() => {
+ return {
+ className: slots.alertIcon({class: classNames?.alertIcon}),
+ };
+ }, [slots, classNames?.alertIcon]);
+
+ return {
+ title,
+ description,
+ isClosable,
+ domRef,
+ getBaseProps,
+ getMainWrapperProps,
+ getDescriptionProps,
+ getTitleProps,
+ color: variantProps["color"],
+ getCloseButtonProps,
+ handleClose,
+ isVisible,
+ onClose,
+ getAlertIconProps,
+ };
+}
diff --git a/packages/components/alert/stories/alert.stories.tsx b/packages/components/alert/stories/alert.stories.tsx
new file mode 100644
index 0000000000..de8f01c413
--- /dev/null
+++ b/packages/components/alert/stories/alert.stories.tsx
@@ -0,0 +1,122 @@
+import React from "react";
+import {Meta} from "@storybook/react";
+import {alert} from "@nextui-org/theme";
+
+import {Alert} from "../src";
+
+export default {
+ title: "Components/Alert",
+ component: Alert,
+ argTypes: {
+ color: {
+ control: {
+ type: "select",
+ },
+ options: ["default", "primary", "secondary", "success", "warning", "danger"],
+ },
+ radius: {
+ control: {
+ type: "select",
+ },
+ options: ["none", "sm", "md", "lg", "full"],
+ },
+ isClosable: {
+ control: {
+ type: "boolean",
+ },
+ },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} as Meta;
+
+const defaultProps = {
+ ...alert.defaultVariants,
+ title: "Email Sent!!",
+ description: "You will get a reply soon",
+};
+
+const Template = (args) => (
+ <>
+
+ >
+);
+
+const ColorTemplate = (args) => {
+ return (
+
+ {["default", "primary", "secondary", "success", "warning", "danger"].map((color) => (
+
+ ))}
+
+ );
+};
+
+const RadiusTemplate = (args) => {
+ return (
+
+ {["none", "sm", "md", "lg", "full"].map((radius) => (
+
+ ))}
+
+ );
+};
+
+export const Default = {
+ render: Template,
+ args: {
+ ...defaultProps,
+ },
+};
+export const Color = {
+ render: ColorTemplate,
+ args: {
+ ...defaultProps,
+ },
+};
+export const Radius = {
+ render: RadiusTemplate,
+ args: {
+ ...defaultProps,
+ },
+};
+export const isClosable = {
+ render: Template,
+ args: {
+ ...defaultProps,
+ isClosable: true,
+ },
+};
+export const CustomWithClassNames = {
+ render: Template,
+ args: {
+ ...defaultProps,
+ classNames: {
+ base: [
+ "bg-slate-100",
+ "border",
+ "shadow",
+ "hover:bg-slate-200",
+ "focus-within:!bg-slate-100",
+ "dark:bg-slate-900",
+ "dark:hover:bg-slate-800",
+ "dark:border-slate-800",
+ "dark:focus-within:!bg-slate-900",
+ "cursor-pointer",
+ ],
+ title: ["text-base", "text-slate-500", "font-bold"],
+ description: ["text-base", "text-slate-500"],
+ },
+ },
+};
diff --git a/packages/components/alert/tsconfig.json b/packages/components/alert/tsconfig.json
new file mode 100644
index 0000000000..5d012f6e61
--- /dev/null
+++ b/packages/components/alert/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "tailwind-variants": ["../../../node_modules/tailwind-variants"]
+ },
+ },
+ "include": ["src", "index.ts"]
+}
diff --git a/packages/components/alert/tsup.config.ts b/packages/components/alert/tsup.config.ts
new file mode 100644
index 0000000000..3e2bcff6cc
--- /dev/null
+++ b/packages/components/alert/tsup.config.ts
@@ -0,0 +1,8 @@
+import {defineConfig} from "tsup";
+
+export default defineConfig({
+ clean: true,
+ target: "es2019",
+ format: ["cjs", "esm"],
+ banner: {js: '"use client";'},
+});
diff --git a/packages/core/react/package.json b/packages/core/react/package.json
index 1dbd682fd3..b701d5d5f7 100644
--- a/packages/core/react/package.json
+++ b/packages/core/react/package.json
@@ -84,6 +84,7 @@
"@nextui-org/date-input": "workspace:*",
"@nextui-org/date-picker": "workspace:*",
"@nextui-org/framer-utils": "workspace:*",
+ "@nextui-org/alert": "workspace:*",
"@react-aria/visually-hidden": "3.8.12"
},
"peerDependencies": {
diff --git a/packages/core/react/src/index.ts b/packages/core/react/src/index.ts
index d504dbd361..96c5475f69 100644
--- a/packages/core/react/src/index.ts
+++ b/packages/core/react/src/index.ts
@@ -43,6 +43,7 @@ export * from "@nextui-org/autocomplete";
export * from "@nextui-org/calendar";
export * from "@nextui-org/date-input";
export * from "@nextui-org/date-picker";
+export * from "@nextui-org/alert";
/**
* React Aria - Exports
diff --git a/packages/core/theme/src/components/alert.ts b/packages/core/theme/src/components/alert.ts
new file mode 100644
index 0000000000..759350f264
--- /dev/null
+++ b/packages/core/theme/src/components/alert.ts
@@ -0,0 +1,107 @@
+import type {VariantProps} from "tailwind-variants";
+
+import {tv} from "../utils/tv";
+/**
+ * Alert wrapper **Tailwind Variants** component
+ *
+ * @example
+ * ```js
+ * const {base, mainWrapper, title, description, closeButton, alertIcon} = alert({...})
+ *
+ *
+ *
+ * {alertIcon}
+ *
+ *
Title
+ *
Description
+ *
+ *
+ *
+ * ```
+ */
+const alert = tv({
+ slots: {
+ base: ["flex flex-row max-w-[342px] flex-grow min-h-17 max-h-full p-3"],
+ mainWrapper: [
+ "flex-grow max-w-[268px] min-h-11 max-h-full ms-5 flex flex-col box-border items-start",
+ ],
+ title: ["w-full text-medium font-normal block min-h-6 max-h-full"],
+ description: ["text-small font-normal min-h-5 max-h-full"],
+ closeButton: ["w-6 h-6 cursor-pointer relative fill-current"],
+ alertIcon: ["fill-current"],
+ },
+ variants: {
+ color: {
+ default: {
+ base: ["bg-default-100"],
+ title: ["text-foreground"],
+ description: ["text-default-600"],
+ closeButton: ["text-default-400"],
+ alertIcon: ["text-default-foreground"],
+ },
+ primary: {
+ base: ["bg-primary-50"],
+ title: ["text-primary"],
+ description: ["text-primary"],
+ closeButton: ["text-primary-200"],
+ alertIcon: ["text-primary"],
+ },
+ secondary: {
+ base: ["bg-secondary-50"],
+ title: ["text-secondary"],
+ description: ["text-secondary-200"],
+ closeButton: ["text-secondary-200"],
+ alertIcon: ["text-secondary"],
+ },
+ success: {
+ base: ["bg-success-50"],
+ title: ["text-success"],
+ description: ["text-success-200"],
+ closeButton: ["text-success-200"],
+ alertIcon: ["text-success"],
+ },
+ warning: {
+ base: ["bg-warning-50"],
+ title: ["text-warning"],
+ description: ["text-warning-200"],
+ closeButton: ["text-warning-200"],
+ alertIcon: ["text-warning"],
+ },
+ danger: {
+ base: ["bg-danger-50"],
+ title: ["text-danger"],
+ description: ["text-danger-200"],
+ closeButton: ["text-danger-200"],
+ alertIcon: ["text-danger"],
+ },
+ },
+ radius: {
+ none: {
+ base: "rounded-none",
+ },
+ sm: {
+ base: "rounded-small",
+ },
+ md: {
+ base: "rounded-medium",
+ },
+ lg: {
+ base: "rounded-large",
+ },
+ full: {
+ base: "rounded-full",
+ },
+ },
+ },
+ defaultVariants: {
+ color: "default",
+ radius: "md",
+ },
+});
+
+export type AlertVariantProps = VariantProps;
+export type AlertSlots = keyof ReturnType;
+
+export {alert};
diff --git a/packages/core/theme/src/components/index.ts b/packages/core/theme/src/components/index.ts
index 7776b24c52..c5ed21c037 100644
--- a/packages/core/theme/src/components/index.ts
+++ b/packages/core/theme/src/components/index.ts
@@ -38,3 +38,4 @@ export * from "./autocomplete";
export * from "./calendar";
export * from "./date-input";
export * from "./date-picker";
+export * from "./alert";
diff --git a/packages/utilities/shared-icons/src/danger.tsx b/packages/utilities/shared-icons/src/danger.tsx
new file mode 100644
index 0000000000..ffa5c56f6d
--- /dev/null
+++ b/packages/utilities/shared-icons/src/danger.tsx
@@ -0,0 +1,20 @@
+import {IconSvgProps} from "./types";
+
+export const DangerIcon = (
+ props: IconSvgProps & {
+ className?: string;
+ },
+) => {
+ return (
+
+ );
+};
diff --git a/packages/utilities/shared-icons/src/index.ts b/packages/utilities/shared-icons/src/index.ts
index d4a719af5c..dc1504605b 100644
--- a/packages/utilities/shared-icons/src/index.ts
+++ b/packages/utilities/shared-icons/src/index.ts
@@ -33,7 +33,10 @@ export * from "./arrow-right";
export * from "./arrow-left";
export * from "./link";
export * from "./selector";
-
+export * from "./info-circle";
+export * from "./warning";
+export * from "./danger";
+export * from "./success";
// sets
export * from "./bulk";
export * from "./bold";
diff --git a/packages/utilities/shared-icons/src/info-circle.tsx b/packages/utilities/shared-icons/src/info-circle.tsx
new file mode 100644
index 0000000000..6f4650d0e3
--- /dev/null
+++ b/packages/utilities/shared-icons/src/info-circle.tsx
@@ -0,0 +1,20 @@
+import {IconSvgProps} from "./types";
+
+export const InfoCircleIcon = (
+ props: IconSvgProps & {
+ className?: string;
+ },
+) => {
+ return (
+
+ );
+};
diff --git a/packages/utilities/shared-icons/src/success.tsx b/packages/utilities/shared-icons/src/success.tsx
new file mode 100644
index 0000000000..dab41491ea
--- /dev/null
+++ b/packages/utilities/shared-icons/src/success.tsx
@@ -0,0 +1,27 @@
+import {IconSvgProps} from "./types";
+
+export const SuccessIcon = (
+ props: IconSvgProps & {
+ className?: string;
+ },
+) => {
+ return (
+
+ );
+};
diff --git a/packages/utilities/shared-icons/src/warning.tsx b/packages/utilities/shared-icons/src/warning.tsx
new file mode 100644
index 0000000000..a647c6b04e
--- /dev/null
+++ b/packages/utilities/shared-icons/src/warning.tsx
@@ -0,0 +1,20 @@
+import {IconSvgProps} from "./types";
+
+export const WarningIcon = (
+ props: IconSvgProps & {
+ className?: string;
+ },
+) => {
+ return (
+
+ );
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 11ec4f64e6..f47f92870e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -678,6 +678,34 @@ importers:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
+ packages/components/alert:
+ dependencies:
+ '@nextui-org/react-utils':
+ specifier: workspace:*
+ version: link:../../utilities/react-utils
+ '@nextui-org/shared-icons':
+ specifier: workspace:*
+ version: link:../../utilities/shared-icons
+ '@nextui-org/shared-utils':
+ specifier: workspace:*
+ version: link:../../utilities/shared-utils
+ devDependencies:
+ '@nextui-org/system':
+ specifier: workspace:*
+ version: link:../../core/system
+ '@nextui-org/theme':
+ specifier: workspace:*
+ version: link:../../core/theme
+ clean-package:
+ specifier: 2.2.0
+ version: 2.2.0
+ react:
+ specifier: ^18.2.0
+ version: 18.2.0
+ react-dom:
+ specifier: ^18.2.0
+ version: 18.2.0(react@18.2.0)
+
packages/components/autocomplete:
dependencies:
'@nextui-org/aria-utils':
@@ -2831,6 +2859,9 @@ importers:
'@nextui-org/accordion':
specifier: workspace:*
version: link:../../components/accordion
+ '@nextui-org/alert':
+ specifier: workspace:*
+ version: link:../../components/alert
'@nextui-org/autocomplete':
specifier: workspace:*
version: link:../../components/autocomplete