From 95f77173299753192ae0928d7996be2e961c09fc Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Fri, 19 Jun 2026 18:01:56 +0200
Subject: [PATCH] feat: add Alert component and migrate alert banners to it
Add a reusable Alert component (@librechat/client) with error/success/warning/
info/neutral variants backed by the status-color tokens, default per-variant
icons, and role=alert. Migrate the duplicated colored-div banners to it:
Auth ErrorMessage, RequestPasswordReset success, and the identical error boxes
in ToolSelectDialog, AssistantToolsDialog, and MCPToolSelectDialog.
---
client/src/components/Auth/ErrorMessage.tsx | 11 ++--
.../components/Auth/RequestPasswordReset.tsx | 9 +--
.../components/Tools/AssistantToolsDialog.tsx | 8 +--
packages/client/src/components/Alert.tsx | 56 +++++++++++++++++++
packages/client/src/components/index.ts | 1 +
5 files changed, 70 insertions(+), 15 deletions(-)
create mode 100644 packages/client/src/components/Alert.tsx
diff --git a/client/src/components/Auth/ErrorMessage.tsx b/client/src/components/Auth/ErrorMessage.tsx
index 383ff9a930..562212bda9 100644
--- a/client/src/components/Auth/ErrorMessage.tsx
+++ b/client/src/components/Auth/ErrorMessage.tsx
@@ -1,9 +1,12 @@
+import { Alert } from '@librechat/client';
+
export const ErrorMessage = ({ children }: { children: React.ReactNode }) => (
-
{children}
-
+
);
diff --git a/client/src/components/Auth/RequestPasswordReset.tsx b/client/src/components/Auth/RequestPasswordReset.tsx
index 10996847fe..d95954cf00 100644
--- a/client/src/components/Auth/RequestPasswordReset.tsx
+++ b/client/src/components/Auth/RequestPasswordReset.tsx
@@ -1,6 +1,6 @@
import { useForm } from 'react-hook-form';
import { useState, ReactNode } from 'react';
-import { Spinner, Button } from '@librechat/client';
+import { Spinner, Button, Alert } from '@librechat/client';
import { useOutletContext } from 'react-router-dom';
import { useRequestPasswordResetMutation } from 'librechat-data-provider/react-query';
import { loginPage } from 'librechat-data-provider';
@@ -11,12 +11,9 @@ import { useLocalize } from '~/hooks';
const BodyTextWrapper: FC<{ children: ReactNode }> = ({ children }) => {
return (
-
+
);
};
diff --git a/client/src/components/Tools/AssistantToolsDialog.tsx b/client/src/components/Tools/AssistantToolsDialog.tsx
index 98e8043ebe..7124afe5af 100644
--- a/client/src/components/Tools/AssistantToolsDialog.tsx
+++ b/client/src/components/Tools/AssistantToolsDialog.tsx
@@ -1,5 +1,6 @@
import { useEffect } from 'react';
import { Search, X } from 'lucide-react';
+import { Alert } from '@librechat/client';
import { Dialog, DialogPanel, DialogTitle, Description } from '@headlessui/react';
import { useFormContext } from 'react-hook-form';
import { isAgentsEndpoint } from 'librechat-data-provider';
@@ -186,12 +187,9 @@ function AssistantToolsDialog({
{error && (
-
+
{localize('com_nav_plugin_auth_error')} {errorMessage}
-
+
)}
{showPluginAuthForm && (
diff --git a/packages/client/src/components/Alert.tsx b/packages/client/src/components/Alert.tsx
new file mode 100644
index 0000000000..49155d1c23
--- /dev/null
+++ b/packages/client/src/components/Alert.tsx
@@ -0,0 +1,56 @@
+import * as React from 'react';
+import { Info, AlertCircle, AlertTriangle, CheckCircle2 } from 'lucide-react';
+import { cva, type VariantProps } from 'class-variance-authority';
+import { cn } from '~/utils';
+
+const alertVariants = cva('relative flex gap-3 rounded-xl border px-4 py-3 text-sm', {
+ variants: {
+ variant: {
+ info: 'border-status-info bg-status-info-subtle text-status-info',
+ success: 'border-status-success bg-status-success-subtle text-status-success',
+ warning: 'border-status-warning bg-status-warning-subtle text-status-warning',
+ error: 'border-status-error bg-status-error-subtle text-status-error',
+ neutral: 'border-status-neutral bg-status-neutral-subtle text-status-neutral',
+ },
+ },
+ defaultVariants: {
+ variant: 'info',
+ },
+});
+
+type AlertVariant = NonNullable
['variant']>;
+
+const defaultIcons: Record> = {
+ info: Info,
+ success: CheckCircle2,
+ warning: AlertTriangle,
+ error: AlertCircle,
+ neutral: Info,
+};
+
+export interface AlertProps
+ extends React.HTMLAttributes,
+ VariantProps {
+ /** Override the default per-variant icon, or pass `false` to hide it. */
+ icon?: React.ReactNode | false;
+}
+
+const Alert = React.forwardRef(
+ ({ className, variant = 'info', icon, role = 'alert', children, ...props }, ref) => {
+ const resolvedVariant = (variant ?? 'info') as AlertVariant;
+ const DefaultIcon = defaultIcons[resolvedVariant];
+ return (
+
+ {icon !== false && (
+
+ {icon ?? }
+
+ )}
+
{children}
+
+ );
+ },
+);
+Alert.displayName = 'Alert';
+
+export { Alert, alertVariants };
diff --git a/packages/client/src/components/index.ts b/packages/client/src/components/index.ts
index e3e66b7559..0080307858 100644
--- a/packages/client/src/components/index.ts
+++ b/packages/client/src/components/index.ts
@@ -1,5 +1,6 @@
export * from './Accordion';
export * from './AnimatedTabs';
+export * from './Alert';
export * from './AlertDialog';
export * from './Breadcrumb';
export * from './Button';