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#

Prompt input#

On desktop, Enter sends the message and Shift+Enter adds a new line. On touch-primary devices, Enter adds a new line and the send button submits.

Du har brukt 0 av 111 tegn.

<PromptInput characterCounter={111} />

Compact prompt input#

<PromptInput variant="compact" placeholder="Ask Aino" />

Inline compact prompt input#

Use dropShadow={false} for the flat inline surface.

<PromptInput
  variant="compact"
  dropShadow={false}
  placeholder="Ask Aino"
/>

Custom actions#

Compose attachment and microphone buttons with startActions and endActions. Here, attachment adds an example document and microphone inserts example dictated text. Connect these controls to your application's file picker and speech recognition.

const CustomActions = () => {
  const [value, setValue] = useState('')
  const [hasAttachment, setHasAttachment] = useState(false)
  return (
    <PromptInput
      variant={variant}
      icon={null}
      value={value}
      onChange={({ value }) => setValue(value)}
      onSubmit={() => setValue('')}
      startActions={
        <Button
          type="button"
          variant="tertiary"
          bounding
          icon={add}
          iconSize="default"
          aria-label="Add attachment"
          tooltip="Add attachment"
          disabled={hasAttachment}
          onClick={() => setHasAttachment(true)}
        />
      }
      endActions={
        <Button
          type="button"
          variant="tertiary"
          bounding
          icon={microphone}
          iconSize="default"
          aria-label="Use microphone"
          tooltip="Use microphone"
          onClick={() => setValue('Show my latest transactions')}
        />
      }
    >
      {hasAttachment && (
        <Button
          type="button"
          variant="tertiary"
          bounding
          icon={close}
          iconSize="default"
          text="Budget.pdf"
          aria-label="Remove Budget.pdf"
          onClick={() => setHasAttachment(false)}
        />
      )}
    </PromptInput>
  )
}
render(<CustomActions />)
const CustomActions = () => {
  const [value, setValue] = useState('')
  const [hasAttachment, setHasAttachment] = useState(false)
  return (
    <PromptInput
      variant={variant}
      icon={null}
      value={value}
      onChange={({ value }) => setValue(value)}
      onSubmit={() => setValue('')}
      startActions={
        <Button
          type="button"
          variant="tertiary"
          bounding
          icon={add}
          iconSize="default"
          aria-label="Add attachment"
          tooltip="Add attachment"
          disabled={hasAttachment}
          onClick={() => setHasAttachment(true)}
        />
      }
      endActions={
        <Button
          type="button"
          variant="tertiary"
          bounding
          icon={microphone}
          iconSize="default"
          aria-label="Use microphone"
          tooltip="Use microphone"
          onClick={() => setValue('Show my latest transactions')}
        />
      }
    >
      {hasAttachment && (
        <Button
          type="button"
          variant="tertiary"
          bounding
          icon={close}
          iconSize="default"
          text="Budget.pdf"
          aria-label="Remove Budget.pdf"
          onClick={() => setHasAttachment(false)}
        />
      )}
    </PromptInput>
  )
}
render(<CustomActions />)

Prompt input with context#

Pass your context as children. The application owns the reference and its removal. The custom action adds it again after removal.

dnb.no/...Insurance
const ContextChip = styled.div`
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0.5rem;
  background: var(--token-color-background-neutral-subtle);
  border: 0.0625rem solid var(--token-color-stroke-neutral-subtle);
  border-radius: 0.5rem;
`
const InputWithContext = () => {
  const [hasReference, setHasReference] = useState(true)
  const [value, setValue] = useState(
    'Add the companies from the attached document to my watchlist.'
  )
  return (
    <PromptInput
      dropShadow
      value={value}
      onChange={({ value }) => setValue(value)}
      onSubmit={() => setValue('')}
      startActions={
        <Button
          type="button"
          variant="tertiary"
          size="small"
          icon={add}
          iconSize="default"
          aria-label="Add reference"
          tooltip="Add reference"
          disabled={hasReference}
          onClick={() => setHasReference(true)}
        />
      }
    >
      {hasReference && (
        <ContextChip>
          <Avatar size="small" src={referenceAvatar} aria-hidden />
          <span>dnb.no/...Insurance</span>
          <Button
            type="button"
            variant="tertiary"
            size="small"
            icon={close}
            iconSize="default"
            aria-label="Remove reference"
            onClick={() => setHasReference(false)}
          />
        </ContextChip>
      )}
    </PromptInput>
  )
}
render(<InputWithContext />)

Stop while streaming#

Set isBusy and pass onStop to show a stop button while the reply streams in.

const Chat = () => {
  const [isBusy, setIsBusy] = useState(false)
  useEffect(() => {
    if (!isBusy) {
      return // stop here
    }

    const timeout = setTimeout(() => setIsBusy(false), 4000)
    return () => clearTimeout(timeout)
  }, [isBusy])
  return (
    <PromptInput
      variant={variant}
      isBusy={isBusy}
      onSubmit={() => setIsBusy(true)}
      onStop={() => setIsBusy(false)}
    />
  )
}
render(<Chat />)
const Chat = () => {
  const [isBusy, setIsBusy] = useState(false)
  useEffect(() => {
    if (!isBusy) {
      return // stop here
    }

    const timeout = setTimeout(() => setIsBusy(false), 4000)
    return () => clearTimeout(timeout)
  }, [isBusy])
  return (
    <PromptInput
      variant={variant}
      isBusy={isBusy}
      onSubmit={() => setIsBusy(true)}
      onStop={() => setIsBusy(false)}
    />
  )
}
render(<Chat />)

Prompt input with an error#

The message could not be sent. Try again.
<PromptInput
  textareaProps={{
    status: 'The message could not be sent. Try again.',
  }}
/>

Disabled prompt input#

<PromptInput
  disabled
  value="Message unavailable"
  startActions={
    <Button
      type="button"
      variant="tertiary"
      size="small"
      text="Add reference"
      disabled
    />
  }
/>
Suggest an edit