Skip to content

Experimental: The AI extension and its component APIs may change in the future. Please give us feedback on your experience with them.

Demos#

See Message scroller for demos of how the conversation scrolls.

Conversation with date and info#

Scroll up to see the button that takes you to the latest message.


Aino is a chatbot for customer service. Do not share personal information with the chatbot. Read more about your privacy.

Some answers are generated with artificial intelligence and are marked with a tag. AI-generated answers can contain errors.

Aino - 14:32
Hi, I am Aino, the digital assistant of DNB.
How can I help you?
You - 14:33
I see an amount withdrawn from my account that I do not recognize. Can you help me find out what it is?
Aino - 14:33KI-generert
Of course. Choose the transaction you want to know more about, and I will show you the details.
<Conversation
  style={{
    height: '24rem',
  }}
>
  <DateMarker date="2026-02-02" />
  <Info>
    <P>
      Aino is a chatbot for customer service. Do not share personal
      information with the chatbot. Read more about{' '}
      <Anchor href="https://www.dnb.no/personvern">your privacy</Anchor>.
    </P>
    <P top>
      Some answers are generated with artificial intelligence and are
      marked with a tag. AI-generated answers can contain errors.
    </P>
  </Info>
  <Message name="Aino" timestamp="14:32">
    Hi, I am Aino, the digital assistant of DNB.
  </Message>
  <Message>How can I help you?</Message>
  <Message from="user" name="You" timestamp="14:33">
    I see an amount withdrawn from my account that I do not recognize. Can
    you help me find out what it is?
  </Message>
  <Message name="Aino" timestamp="14:33" aiGenerated>
    Of course. Choose the transaction you want to know more about, and I
    will show you the details.
  </Message>
</Conversation>

Reading while content changes#

Scroll to earlier messages, then send a new prompt or extend the reply. A new user prompt returns to the end; reading or selecting older content pauses following. Resizing the prompt keeps the latest reply visible when you are following it. Receive message adds an incoming reply; while reading earlier messages, the scroll button shows “New message” and the hook reports unread messages.

Unread messages: no

Message 0. Read earlier messages while the reply grows. Message 0. Read earlier messages while the reply grows. Message 0. Read earlier messages while the reply grows. Message 0. Read earlier messages while the reply grows.
Message 1. Read earlier messages while the reply grows. Message 1. Read earlier messages while the reply grows. Message 1. Read earlier messages while the reply grows. Message 1. Read earlier messages while the reply grows.
Message 2. Read earlier messages while the reply grows. Message 2. Read earlier messages while the reply grows. Message 2. Read earlier messages while the reply grows. Message 2. Read earlier messages while the reply grows.
Message 3. Read earlier messages while the reply grows. Message 3. Read earlier messages while the reply grows. Message 3. Read earlier messages while the reply grows. Message 3. Read earlier messages while the reply grows.
Message 4. Read earlier messages while the reply grows. Message 4. Read earlier messages while the reply grows. Message 4. Read earlier messages while the reply grows. Message 4. Read earlier messages while the reply grows.
Message 5. Read earlier messages while the reply grows. Message 5. Read earlier messages while the reply grows. Message 5. Read earlier messages while the reply grows. Message 5. Read earlier messages while the reply grows.
Message 6. Read earlier messages while the reply grows. Message 6. Read earlier messages while the reply grows. Message 6. Read earlier messages while the reply grows. Message 6. Read earlier messages while the reply grows.
Message 7. Read earlier messages while the reply grows. Message 7. Read earlier messages while the reply grows. Message 7. Read earlier messages while the reply grows. Message 7. Read earlier messages while the reply grows.
Message 8. Read earlier messages while the reply grows. Message 8. Read earlier messages while the reply grows. Message 8. Read earlier messages while the reply grows. Message 8. Read earlier messages while the reply grows.
Message 9. Read earlier messages while the reply grows. Message 9. Read earlier messages while the reply grows. Message 9. Read earlier messages while the reply grows. Message 9. Read earlier messages while the reply grows.
Message 10. Read earlier messages while the reply grows. Message 10. Read earlier messages while the reply grows. Message 10. Read earlier messages while the reply grows. Message 10. Read earlier messages while the reply grows.
Message 11. Read earlier messages while the reply grows. Message 11. Read earlier messages while the reply grows. Message 11. Read earlier messages while the reply grows. Message 11. Read earlier messages while the reply grows.
const UnreadState = () => {
  const { hasUnreadMessages } = useConversationScrollState('reading-chat')
  return <P>Unread messages: {hasUnreadMessages ? 'yes' : 'no'}</P>
}
const Reading = () => {
  const [messages, setMessages] = useState(
    Array.from(
      {
        length: 12,
      },
      (_, index) => ({
        id: 'reading-' + index,
        from: index % 2 ? ('assistant' as const) : ('user' as const),
        text: (
          'Message ' +
          index +
          '. Read earlier messages while the reply grows. '
        ).repeat(4),
      })
    )
  )
  return (
    <>
      <Flex.Horizontal bottom>
        <Button
          onClick={() =>
            setMessages((current) =>
              current.map((message, index) =>
                index === current.length - 1
                  ? {
                      ...message,
                      text:
                        message.text + ' More reply content.'.repeat(12),
                    }
                  : message
              )
            )
          }
        >
          Extend reply
        </Button>
        <Button
          onClick={() =>
            setMessages((current) => [
              ...current,
              {
                id: 'reading-' + current.length,
                from: 'assistant',
                text: 'A new message from customer service.',
              },
            ])
          }
        >
          Receive message
        </Button>
      </Flex.Horizontal>
      <UnreadState />
      <div
        style={{
          display: 'flex',
          flexDirection: 'column',
          gap: '1rem',
          height: '28rem',
        }}
      >
        <Conversation
          id="reading-chat"
          scrollBehavior="end"
          aria-live="off"
          style={{
            flex: '1 1 auto',
          }}
        >
          {messages.map((message) => (
            <Message
              key={message.id}
              id={message.id}
              from={message.from}
              actions={
                message.from === 'assistant' && (
                  <Button size="small">Reply details</Button>
                )
              }
            >
              {message.text}
            </Message>
          ))}
        </Conversation>
        <PromptInput
          onSubmit={({ value }) =>
            setMessages((current) => [
              ...current,
              {
                id: 'reading-' + current.length,
                from: 'user',
                text: value,
              },
              {
                id: 'reading-' + (current.length + 1),
                from: 'assistant',
                text: 'Your next reply starts here.',
              },
            ])
          }
        />
      </div>
    </>
  )
}
render(<Reading />)

Virtualized history#

This opt-in example keeps a window of 1,000 variable-height messages mounted. Try navigating to message 100, appending a turn, extending the latest reply and prepending history. Use Show full transcript for browser search, selection across the full conversation and screen-reader access to unmounted messages.

The example has no real requests. The application owns reply announcements. See Virtualization for the tradeoffs and driver import.

Current turn: none

Unread messages: no

You
Message 0. A longer reply with details.
Aino
Message 1. A longer reply with details. A longer reply with details.
You
Message 2. A longer reply with details. A longer reply with details. A longer reply with details.
Aino
Message 3. A longer reply with details. A longer reply with details. A longer reply with details. A longer reply with details.
You
Message 4. A longer reply with details.
Aino
Message 5. A longer reply with details. A longer reply with details.
You
Message 6. A longer reply with details. A longer reply with details. A longer reply with details.
Aino
Message 7. A longer reply with details. A longer reply with details. A longer reply with details. A longer reply with details.
You
Message 8. A longer reply with details.
Aino
Message 9. A longer reply with details. A longer reply with details.
You
Message 10. A longer reply with details. A longer reply with details. A longer reply with details.
<VirtualizedDemo />
Suggest an edit