Inserts a chat UI element into a Shiny UI, which includes a scrollable section for displaying chat messages, and an input field for the user to enter new messages.
To respond to user input, listen for input$ID_user_input (for example, if
id="my_chat", user input will be at input$my_chat_user_input), and use
chat_append() to append messages to the chat.
Usage
chat_ui(
id,
...,
messages = NULL,
greeting = NULL,
placeholder = "Enter a message...",
width = "min(680px, 100%)",
height = "auto",
fill = TRUE,
icon_assistant = NULL,
enable_cancel = FALSE,
footer = NULL
)Arguments
- id
The ID of the chat element
- ...
Extra HTML attributes to include on the chat element
- messages
A list of messages to prepopulate the chat with. Each message can be one of the following:
A string, which is interpreted as markdown and rendered to HTML on the client.
To prevent interpreting as markdown, mark the string as
htmltools::HTML().
A UI element.
This includes
htmltools::tagList(), which take UI elements (including strings) as children. In this case, strings are still interpreted as markdown as long as they're not inside HTML.
A named list of
contentandrole. Thecontentcan contain content as described above, and therolecan be "assistant" or "user".
- greeting
An optional greeting to display when the chat first loads. Can be a
chat_greeting()object, or a plain string (which is auto-wrapped with default options). The greeting is dismissed when the user sends their first message. For example:greeting = chat_greeting("## Hello!\n\nHow can I help you today?")- placeholder
The placeholder text for the chat's user input field
- width
The CSS width of the chat element
- height
The CSS height of the chat element
- fill
Whether the chat element should try to vertically fill its container, if the container is fillable
- icon_assistant
The icon to use for the assistant chat messages. Can be HTML or a tag in the form of
htmltools::HTML()orhtmltools::tags(). IfNone, a default robot icon is used.- enable_cancel
If
TRUE, show a stop button during streaming that allows the user to cancel the in-progress response. When usingchat_mod_server(), cancellation is wired up automatically. For manual usage withchat_ui(), observeinput$<id>_cancelto handle cancellation (e.g., by callingctrl$cancel()on an ellmerstream_controller()). Defaults toFALSE.Optional HTML content to display below the chat input. This can be any HTML content (tags, tag lists, or character strings). Useful for adding disclaimers, attribution, or other information. The footer text is styled slightly smaller and lighter than body text by default. Customize with CSS properties
--shiny-chat-footer-font-sizeand--shiny-chat-footer-coloron the chat container or footer element.
Greeting
A greeting is an optional welcome message shown before any conversation
messages. It is automatically dismissed when the user sends their first
message (unless created with dismissible = FALSE).
Static greeting. Pass a string or chat_greeting() to the greeting
parameter:
chat_ui("chat", greeting = "## Hello!\n\nHow can I help you today?")Dynamic greeting from the server. Leave greeting unset and use
chat_set_greeting() from your server function. This is useful when the
greeting depends on session state or is generated by a model.
greeting_requested input. When the chat is visible on the page, has
no messages, and has no greeting set, Shiny fires
input$<id>_greeting_requested (e.g. input$chat_greeting_requested for
chat_ui("chat")). The value is an event counter suitable for
shiny::observeEvent(). Use it to trigger server-side greeting generation:
observeEvent(input$chat_greeting_requested, {
stream <- chat_client$stream_async("Generate a short welcome message.")
chat_set_greeting("chat", chat_greeting(stream))
})This input fires when the chat component is first viewed on the page and
empty, and again after chat_clear() (greeting = TRUE), enabling a
regenerate pattern where clearing the greeting automatically triggers a
fresh one.
Thinking display
When a model produces reasoning or "thinking" tokens, shinychat renders them in a collapsible panel above the response. The panel shows a live stream of the model's reasoning while it thinks, then auto-collapses when the response begins.
Thinking display works automatically with any model that supports it. Two paths are supported:
ellmer's
ContentThinkingobjects. Models that provide a structured thinking API (e.g., Claude with extended thinking) emitContentThinkingobjects when you stream withstream = "content". shinychat detects these and routes them to the thinking panel. This is whatchat_append()uses internally when you pass it an ellmer content stream.Raw
<thinking>tags. Many open-source and local models (DeepSeek, QwQ, Qwen, etc.) emit<thinking>...</thinking>tags directly in their markdown output. shinychat detects these tags during streaming and renders the enclosed text in the thinking panel with no extra configuration.
Topic labels
You can optionally get labeled sub-sections within the thinking panel by
asking the model to emit <topic>...</topic> tags in its reasoning. These
are extracted and rendered as section headings inside the thinking display,
and the current topic appears in the collapsed header as a live status.
To use topic labels, add something like this to your system prompt:
When thinking through a problem, wrap brief topic labels in <topic> tags
to indicate what you're currently reasoning about. For example:
<topic>parsing the input</topic>Topic labels are entirely optional. Without them, the thinking panel still works – it just won't have sub-section headings.
Examples
if (FALSE) { # interactive()
library(shiny)
library(bslib)
library(shinychat)
ui <- page_fillable(
chat_ui("chat", fill = TRUE)
)
server <- function(input, output, session) {
observeEvent(input$chat_user_input, {
# In a real app, this would call out to a chat client or API,
# perhaps using the 'ellmer' package.
response <- paste0(
"You said:\n\n",
"<blockquote>",
htmltools::htmlEscape(input$chat_user_input),
"</blockquote>"
)
chat_append("chat", response)
chat_append("chat", stream)
})
}
shinyApp(ui, server)
}