Experimental: The AI extension and its component APIs may change in the future. Please give us feedback on your experience with them.
Import#
import { Message } from '@dnb/eufemia/extensions/ai'import '@dnb/eufemia/extensions/ai/style'render(<Message />)
Description#
Bubble messages use theme colors and radius, small text and 0.5rem by 1rem padding. Avatars sit beside the bubble. Use the same avatar with the grouped prop on follow-up messages to retain alignment without repeating the icon; user avatars, names and timestamps are optional.
For a customer-service handover, use from="assistant", an Avatar with the customer_service icon and omit aiGenerated. This represents the conversation side without labeling a person as AI.
Ai.Message shows a message from the user or the assistant, with an optional header (avatar, name, time and an AI-generated tag) and actions. User messages are placed on the right.
Compose the content inside Ai.Message using children: plain text, Ai.Response for markdown, Ai.Task, or Ai.Sources. String children preserve line breaks. Set from="user" for user messages and give each message a unique DOM id to use the conversation scrolling and visibility hooks.
See Integrations for connecting your chat data to these components.
Demos#
Chat bubbles and sender groups#
<Conversation scrollBehavior="end" style={{ height: '24rem', maxWidth: '23rem', }} > <Message from="user">Chatmessage from the user.</Message> <Message aria-label="Aino" avatar={<Avatar size="small" icon={aino} hasLabel />} > Chatmessage from a bot. <br /> Beep-bop. </Message> <Message grouped avatar={<Avatar size="small" icon={aino} hasLabel />}> <Response>More information from the same bot.</Response> </Message> <Message from="user"> Thanks. This longer message wraps before reaching the opposite edge of the conversation. </Message> <Message avatar={<Avatar size="small" icon={customer_service} hasLabel />} > Hello from customer service. </Message> </Conversation>
Plain message#
Content like a list can be shown without a bubble by using variant="plain".
<Message variant="plain" name="Solve" timestamp="15:24" avatar={ <Avatar variant="secondary" hasLabel> S </Avatar> } aiGenerated > <Response> { 'It seems you made **2 transfers** 12.02. Choose the correct one below.' } </Response> </Message>
Message actions#
const Feedback = () => { const [feedback, setFeedback] = useState<'positive' | 'negative' | null>( null ) return ( <Message name="Aino" actions={ <Actions role="group" aria-label="Answer feedback"> <CopyOnClick.Button size="small" iconSize="default" bounding copyContent="Your card is now blocked." title="Copy answer" tooltip="Copy answer" /> <Action icon={thumbs_up} label="Good answer" selected={feedback === 'positive'} onClick={() => setFeedback(feedback === 'positive' ? null : 'positive') } /> <Action icon={thumbs_down} label="Bad answer" selected={feedback === 'negative'} onClick={() => setFeedback(feedback === 'negative' ? null : 'negative') } /> </Actions> } > Your card is now blocked. </Message> ) } render(<Feedback />)
Composing message content#
Compose the content inside Ai.Message using children.
<Message from="user">What did I spend on food?</Message> <Message> <Task title="Checking your transactions" status="done" /> <Response parseIncompleteMarkdown={false}> You spent **4 200 kr** on groceries in March. </Response> <Sources sources={[ { url: 'https://www.dnb.no/privat/dagligbank', title: 'Everyday banking', }, ]} /> </Message>