{"id":34023949,"url":"https://github.com/gbolly/dash-chat","last_synced_at":"2026-04-06T07:01:42.766Z","repository":{"id":269047283,"uuid":"898915189","full_name":"gbolly/dash-chat","owner":"gbolly","description":"Chat component for Python Dash","archived":false,"fork":false,"pushed_at":"2025-07-07T01:13:18.000Z","size":8955,"stargazers_count":31,"open_issues_count":6,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-09T18:12:52.807Z","etag":null,"topics":["chat","chatbot","dash","plotly-dash","python"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gbolly.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-12-05T09:25:36.000Z","updated_at":"2026-02-24T10:19:49.000Z","dependencies_parsed_at":"2024-12-20T15:22:34.536Z","dependency_job_id":"61526914-83e7-46c2-b400-2cbde0ee0858","html_url":"https://github.com/gbolly/dash-chat","commit_stats":null,"previous_names":["gbolly/dash-chat"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/gbolly/dash-chat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbolly%2Fdash-chat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbolly%2Fdash-chat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbolly%2Fdash-chat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbolly%2Fdash-chat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gbolly","download_url":"https://codeload.github.com/gbolly/dash-chat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbolly%2Fdash-chat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31463015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"online","status_checked_at":"2026-04-06T02:00:07.287Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chat","chatbot","dash","plotly-dash","python"],"created_at":"2025-12-13T16:03:46.659Z","updated_at":"2026-04-06T07:01:42.758Z","avatar_url":"https://github.com/gbolly.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dash-chat\n\n[![PyPI version](https://badge.fury.io/py/dash-chat.svg)](https://pypi.org/project/dash-chat/)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/dash-chat.svg)](https://pypi.org/project/dash-chat/)\n[![CircleCI](https://dl.circleci.com/status-badge/img/gh/gbolly/dash-chat/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/gbolly/dash-chat/tree/main)\n\ndash-chat is a Dash component library chat interface. It provides a customizable and responsive chat UI with support for markdown, chat persistence, typing indicators, themes, and state management.\n\n## Installation\n```\n$ pip install dash-chat\n```\n\n## Basic Usage\nThe simplest way to use the `dash_chat.ChatComponent` is to initialize the `messages` prop as an empty list. This is a list of messages that initialize the chat UI. Each message is an OpenAI-style dictionary that must have the following key-value pairs:\n- `role`: The message sender, either `\"user\"` or `\"assistant\"`.\n- `content`: The content of the message.\n\nA dash callback chat function is also required to handle how the messages are updated\n\n### Example 1\nUsing **OpenAI** with dash-chat (requires the `openai` package - install it by running `pip install openai`)\n\n![dash-chat-demo](https://github.com/gbolly/dash-chat/blob/main/demo-gifs/dash-chat-demo.gif?raw=true)\n\n```python\nimport os\nimport dash\nfrom dash import callback, html, Input, Output, State\nfrom dash_chat import ChatComponent\nfrom openai import OpenAI\n\n\napi_key = os.environ.get(\"OPENAI_API_KEY\")\nclient = OpenAI(api_key=api_key)\n\napp = dash.Dash(__name__)\n\napp.layout = html.Div([\n    ChatComponent(\n        id=\"chat-component\",\n        messages=[],\n    )\n])\n\n@callback(\n    Output(\"chat-component\", \"messages\"),\n    Input(\"chat-component\", \"new_message\"),\n    State(\"chat-component\", \"messages\"),\n    prevent_initial_call=True,\n)\ndef handle_chat(new_message, messages):\n    if not new_message:\n        return messages\n\n    updated_messages = messages + [new_message]\n\n    if new_message[\"role\"] == \"user\":\n        response = client.chat.completions.create(\n            model=\"gpt-3.5-turbo\",\n            messages=updated_messages,\n            temperature=1.0,\n            max_tokens=150,\n        )\n\n        bot_response = {\"role\": \"assistant\", \"content\": response.choices[0].message.content.strip()}\n        return updated_messages + [bot_response]\n\n    return updated_messages\n\nif __name__ == \"__main__\":\n    app.run(debug=True)\n```\n\n### Example 2\nTo send local images and files along with a message to the AI assistant, the structure of `content` in the `messages` prop becomes a list of dictionary. The `content` takes the structure;\n\n```python\n    [\n        {\"type\": \"text\", \"text\": \"Analyze image\"},\n        {\n            \"type\": \"attachment\",\n            \"file\": \u003cbase64File\u003e,\n            \"fileName\": \u003cfile.name\u003e,\n            \"fileType\": \u003cfile.type\u003e\n        },\n    ]\n```\nIn your dash callback, follow the OpenAI-style for uploading images with text.\n\n![dash-chat-with-image-demo](https://github.com/gbolly/dash-chat/blob/main/demo-gifs/dash-image-demo.gif?raw=true)\n\n```python\nimport base64\nimport dash\nimport os\nimport re\nfrom io import BytesIO\nfrom dash import callback, html, Input, Output, State\nfrom dash_chat import ChatComponent\nfrom openai import OpenAI\n\n\napi_key = os.environ.get(\"OPENAI_API_KEY\")\nclient = OpenAI(api_key=api_key)\n\napp = dash.Dash(__name__)\n\napp.layout = html.Div([\n    ChatComponent(\n        id=\"chat-component\",\n        messages=[],\n        supported_input_file_types=[\".png\", \".jpg\", \".pdf\", \".doc\"]\n    )\n])\n\n\ndef decode_base64(data):\n    match = re.match(r\"data:(.*?);base64,(.*)\", data)\n    if match:\n        _, base64_data = match.groups()\n    else:\n        base64_data = data\n\n    missing_padding = len(base64_data) % 4\n    if missing_padding:\n        base64_data += \"=\" * (4 - missing_padding)\n\n    return base64.b64decode(base64_data)\n\n\n@callback(\n    Output(\"chat-component\", \"messages\"),\n    Input(\"chat-component\", \"new_message\"),\n    State(\"chat-component\", \"messages\"),\n    prevent_initial_call=True,\n)\ndef handle_chat(new_message, messages):\n    if not new_message:\n        return messages\n\n    if isinstance(new_message[\"content\"], list):\n        user_content = []\n        for item in new_message[\"content\"]:\n            if item[\"type\"] == \"text\":\n                user_content.append({\"type\": \"text\", \"text\": item[\"text\"]})\n            elif item[\"type\"] == \"attachment\":\n                file_type = item[\"fileType\"]\n                file_path = item[\"file\"]\n                file_name = item[\"fileName\"]\n\n                if file_type.startswith(\"image/\"):\n                    # https://github.com/openai/openai-python#vision\n                    user_content.append(\n                        {\"type\": \"image_url\", \"image_url\": {\"url\": file_path}}\n                    )\n                else:\n                    # other file types (PDF, DOCX, etc.)\n                    # https://github.com/openai/openai-python?tab=readme-ov-file#file-uploads\n                    decoded_bytes = decode_base64(file_path)\n                    uploaded_file = client.files.create(\n                        file=(file_name, BytesIO(decoded_bytes), file_type),\n                        purpose=\"user_data\"\n                    )\n                    user_content.append({\n                        \"type\": \"text\",\n                        \"text\": f\"File '{file_name}' uploaded. ID: {uploaded_file.id}\",\n                    })\n        updated_messages = messages + [{\"role\": \"user\", \"content\": user_content}]\n    else:\n        updated_messages = messages + [new_message]\n\n    if new_message[\"role\"] == \"user\":\n        response = client.chat.completions.create(\n            model=\"gpt-4o-mini\",\n            messages=updated_messages,\n            temperature=1.0,\n            max_tokens=150,\n        )\n\n        bot_response = {\n            \"role\": \"assistant\",\n            \"content\": response.choices[0].message.content.strip(),\n        }\n\n        return updated_messages + [bot_response]\n\n    return updated_messages\n\n\nif __name__ == \"__main__\":\n    app.run(debug=True)\n```\n\n### Example 3\n`ChatComponent` is agnostic about which chatbot or AI assistant technology you're interacting with, so here's an example not using OpenAI\n\n```python\nimport time\nimport dash\nfrom dash import callback, html, Input, Output, State\nfrom dash_chat import ChatComponent\n\n\napp = dash.Dash(__name__)\n\napp.layout = html.Div([\n    ChatComponent(\n        id=\"chat-component\",\n        messages=[],\n    )\n])\n\n@callback(\n    Output(\"chat-component\", \"messages\"),\n    Input(\"chat-component\", \"new_message\"),\n    State(\"chat-component\", \"messages\"),\n    prevent_initial_call=True,\n)\ndef handle_chat(new_message, messages):\n    if not new_message:\n        return messages\n\n    updated_messages = messages + [new_message]\n\n    if new_message[\"role\"] == \"user\":\n        time.sleep(2)\n        bot_response = {\"role\": \"assistant\", \"content\": \"Hello John Doe.\"}\n        return updated_messages + [bot_response]\n\n    return updated_messages\n\nif __name__ == \"__main__\":\n    app.run(debug=True)\n```\n\n### **Persistence Functionality**\nThe ChatComponent supports persistence, allowing messages to be stored and retrieved across page reloads. When persistence=True, messages are saved in the specified storage (localStorage or sessionStorage).\n\nOn initialization, the component checks for stored messages.\nIf stored messages exist, they are loaded; otherwise, an empty message list is used.\nNew messages are automatically saved to storage.\nWhen the page is refreshed, stored messages are restored to maintain chat history.\nTo enable persistence, set:\n\n```python\nChatComponent(\n    id=\"chat-component\",\n    persistence=True,\n    persistence_type=\"local\"  # or \"session\"\n)\n```\n\n### **Renderers (Graphs, Tables, Attachments \u0026 Text)**\n`dash-chat` supports rich content rendering by allowing messages to contain structured content types like graphs, tables, and images. You can render custom content by passing a structured list to the content field of a message.\n\n#### Text\n```python\n{\n    \"role\": \"assistant\",\n    \"content\": {\n        \"type\": \"text\",\n        \"text\": \"This will be rendered as a markdown message\"\n    },\n}\n```\n\n#### Attachments (Images \u0026 Files)\n```python\n{\n    \"role\": \"assistant\",\n    \"content\": {\n        \"type\": \"attachment\",\n        \"file\": \"data:image/png;base64,...\",\n        \"fileName\": \"example.png\",\n        \"fileType\": \"image/png\"\n    }\n}\n```\nRenders an image or a downloadable file preview.\n\n#### Graph\n```python\n{\n    \"role\": \"assistant\",\n    \"content\": {\n        \"type\": \"graph\",\n        \"props\": {\n            \"figure\": {\n                \"data\": [\n                    {\n                        \"x\": [1, 2, 3],\n                        \"y\": [4, 1, 2],\n                        \"type\": \"bar\", \"name\": \"Demo\"\n                    }\n                ],\n                \"layout\": {\"title\": \"Bar Chart\"},\n            },\n            \"config\": {\"displaylogo\": True},\n            \"responsive\": True\n        }\n    }\n}\n```\nRenders an interactive Plotly graph equivalent to [`dcc.Graph`](https://dash.plotly.com/dash-core-components/graph). The props object supports most of the arguments you would pass to a [`dcc.Graph`](https://dash.plotly.com/dash-core-components/graph).\n\n#### Table\n```python\n{\n    \"role\": \"assistant\",\n    \"content\": {\n        \"type\": \"table\",\n        \"header\": [\"Order ID\", \"Item\", \"Quantity\", \"Total\"],\n        \"data\": [\n            [\"#1021\", \"Apple iPhone\", 1, \"$799\"],\n            [\"#1022\", \"Samsung Galaxy\", 2, \"$1398\"]\n        ],\n        \"props\": {\n            \"striped\": True,\n            \"bordered\": True,\n            \"hover\": True,\n            \"responsive\": True,\n            \"size\": \"lg\"\n        }\n    }\n}\n```\nRenders an HTML table. You provide the table by setting:\n\n- header: a list of strings representing the column names.\n    \u003e Example: [\"Order ID\", \"Item\", \"Quantity\", \"Total\"]\n\n- data: a list of rows, where each row is a list of strings (or values) for the cells.\n    \u003e Example: [[\"#1021\", \"Apple iPhone\", 1, \"$799\"], [\"#1022\", \"Samsung Galaxy\", 2, \"$1398\"]]\n\nThe props object supports all the arguments you would pass to [`dbc.Table`](https://dash-bootstrap-components.opensource.faculty.ai/docs/components/table/) in dash-bootstrap-components.\n\n#### Multiple renderers as a list at `\"content\"`\nMultiple supported renderers can also be provided as the assistants' content:\n```python\n{\n    \"role\": \"assistant\",\n    \"content\": [\n        {\"type\": \"text\", \"text\": \"Here's a bar chart of your data.\"},\n        {\n            \"type\": \"graph\",\n            \"props\": {\n                \"figure\": {\n                    \"data\": [{\"x\": [1, 2, 3], \"y\": [4, 1, 2], \"type\": \"bar\", \"name\": \"Demo\"}],\n                    \"layout\": {\"title\": \"Bar Chart\"},\n                }\n                \"config\": {},\n                \"responsive\": True\n            },\n        },\n        {\n            \"type\": \"table\",\n            \"header\": [\"Order ID\", \"Item\", \"Quantity\", \"Total\"],\n            \"data\": [\n                [\"#1021\", \"iPhone 14\", 1, \"$799\"],\n                [\"#1022\", \"Galaxy S22\", 2, \"$1398\"],\n                [\"#1023\", \"Pixel 7\", 1, \"$599\"],\n            ],\n            \"props\": {\n                \"striped\": True\n            },\n        },\n    ]\n}\n```\nFor a complete example of how to setup dash apps and how to uses renderers see the `usage` folder.\n\n### **Props**\n\n`ChatComponent` can be configured with the following properties:\n\n| Prop Name                     | Type                       | Default Value                 | Description                                                                                   |\n|-------------------------------|----------------------------|-------------------------------|-----------------------------------------------------------------------------------------------|\n| **id**                        | `string`                  | `None`                         | Unique identifier for the component, required for Dash callbacks.                             |\n| **container_style**           | `dict`                    | `None`                         | Inline css styles to customize the chat container.                                            |\n| **fill_height**               | `boolean`                 | `True`                         | Whether to vertically fill the screen with the chat container. If `False`, constrains height. |\n| **fill_width**                | `boolean`                 | `True`                         | Whether to horizontally fill the screen with the chat container. If `False`, constrains width.|\n| **input_container_style**     | `dict`                    | `None`                         | Inline css styles for the container holding the message input field.                          |\n| **input_text_style**          | `dict`                    | `None`                         | Inline css styles for the message input field itself.                                         |\n| **messages**                  | `list of dicts`           | `None`                         | List of chat messages. Each message object must include: `role` and `content`. Initialize as an empty list if no on first load.                  |\n| **theme**                     | `string`                  | `\"light\"`                      | Theme for the chat interface. Options: `\"light\"` or `\"dark\"`.                                 |\n| **typing_indicator**          | `string`                  | `\"dots\"`                       | Type of typing indicator. Options: `\"dots\"` (animated dots) or `\"spinner\"` (spinner).         |\n| **user_bubble_style**         | `dict`                    | `{\"backgroundColor\": \"#007bff\", \"color\": \"white\", \"marginLeft\": \"auto\", \"textAlign\": \"right\"}`                                   | Inline css styles to customize the message bubble for user.            |\n| **assistant_bubble_style**    | `dict`                    | `{\"backgroundColor\": \"#f1f0f0\", \"color\": \"black\", \"marginRight\": \"auto\", \"textAlign\": \"left\"}`                                   | Inline css styles to customize the message bubble for assistant.       |\n| **input_placeholder**         | `string`                  | `None`                         | Placeholder text to be used in the input box.                                                 |\n| **class_name**                | `string`                  | `None`                         | Name to use as class attribute on the main chat container.                                    |\n| **persistence**               | `boolean`                 | `False`                        | Whether to store chat messages so that it can be persisted.                                   |\n| **persistence_type**          | `string`                  | `\"local\"`                      | Where chat messages will be stored for persistence. Options: `\"local\"` or `\"session\"`         |\n| **supported_input_file_types**          | `string`                  | `\"*/*\"`                | String or list of file types to support in the file input         |\n| **file_attachment_button_config** | `dict` |  `{\"show\": true, \"label\": \"Attach File\", \"icon\": \"paperclip\", \"icon_position\": \"only\", \"style\": {}, \"className\": \"\"}` | Configuration for the file attachment button.\n| **send_button_config** | `dict` |  `{\"label\": \"Send\", \"icon\": \"paper-plane\", \"icon_position\": \"only\", \"style\": {}, \"className\": \"\"}` | Configuration for the send button.\n\n```\n- show: Whether to show the button.\n- label: Text label for the button.\n- icon: Icon name (\"paper-plane-horizontal\", \"paper-plane\", \"folder\", \"file\", \"paperclip\").\n- icon_position: Position of the icon relative to text (\"left\", \"right\", \"only\").\n- style: Custom styles for the button.\n- className: Additional class names for styling.\n```\n\n## License\n\nThis project is licensed under the MIT License. See the LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgbolly%2Fdash-chat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgbolly%2Fdash-chat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgbolly%2Fdash-chat/lists"}