> ## Documentation Index
> Fetch the complete documentation index at: https://docs.domino.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# App frameworks

> Start a Domino App in Dash, Streamlit, Flask, Shiny, Rust, or static HTML with the packages, App code, and entry point that serve it on port 8888.

Domino runs any web framework that serves HTTP on `0.0.0.0`, port `8888`. Each section below gives one framework's packages, App code, and entry point. Use it in the [Write the entry point](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/develop-and-test#write-the-entry-point) step of Develop and test. Previewing, publishing, and deploying work the same way for every framework.

## Choose a framework

| Framework | Language | Best for | Performance and scaling |
| :- | :- | :- | :- |
| [**Dash**](#dash) | Python | Interactive analytic Apps built on Plotly visualizations, with callbacks for finer control over layout and interaction. | Serve through a production server such as `gunicorn`. Keep session affinity off when autoscaling. |
| [**Streamlit**](#streamlit) | Python | Turning a Python script into a shareable web App with minimal code. | Turn session affinity on when autoscaling; Streamlit keeps session state in server memory. |
| [**Flask**](#flask) | Python | Custom endpoints and fully bespoke web Apps. | Serve through `gunicorn` for production traffic. |
| [**Shiny**](#shiny) | R | R users building reactive web Apps. | Autoscale with session affinity on and small Hardware Tiers. Identity headers require Shiny Server Pro. |
| [**Rust**](#rust) | Rust | Compiled web servers where latency, throughput, or correctness matter more than iteration speed. | Built once with a pinned toolchain and run from the stored binary. The **domino-rust-apps** Skill handles the setup. |
| [**Static HTML**](#static-html) | HTML and JavaScript | Serving prebuilt pages or reports with no server-side logic. | Served by Python's `http.server`. |

For a JavaScript front end such as React, ask a coding assistant to scaffold the App with the **domino-app-init** [Domino Skill](https://docs.domino.ai/6.3/platform-capabilities/features/coding-assistants/domino-skills.md). [Size and scale an App](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/deploy.md#size-and-scale-an-app) covers the performance settings in the table.

## Before you begin

The Project's Compute Environment must include the framework. To add a Python package, open the environment under **Govern** > **Environments**, select **Edit Definition**, add the install to the Dockerfile, and select **Build**. For example, for Dash:

```dockerfile theme={null}
USER root
RUN pip install dash>=4.4.1
USER ubuntu
```

Every framework that accepts a base path or route prefix must read it from `DOMINO_RUN_HOST_PATH`. Domino sets this variable at startup to the URL path it serves the App under.

## Dash

[Dash](https://dash.plotly.com/) builds interactive analytic Apps on Plotly visualizations. Install `dash` in the Compute Environment.

```python app.py theme={null}
import os
import plotly.express as px
from dash import Dash, dcc, html

# Serve under the path Domino assigns to the App
runurl = os.environ.get("DOMINO_RUN_HOST_PATH", "/")

app = Dash(__name__,
           routes_pathname_prefix='/',
           requests_pathname_prefix=runurl)

fig = px.scatter(x=[1, 2, 3], y=[4, 1, 6], title="Sample Scatter Plot")

app.layout = html.Div([
    html.H1("Minimal Dash App"),
    dcc.Graph(figure=fig)
])

if __name__ == '__main__':
    app.run(port=8888, host='0.0.0.0', debug=False)
```

```bash app.sh theme={null}
#!/usr/bin/env bash
python app.py
```

`routes_pathname_prefix='/'` and `requests_pathname_prefix` from `DOMINO_RUN_HOST_PATH` let Dash load its assets and callbacks behind the Domino proxy. The [Dash Gallery](https://dash.gallery) has layouts to adapt.

## Streamlit

[Streamlit](https://docs.streamlit.io/) turns a Python script into a web App. Domino Standard Environments include Streamlit.

Streamlit serves on port `8501` by default, so the entry point writes a Streamlit configuration file that sets port `8888` and host `0.0.0.0` before it starts the App.

```python app.py theme={null}
import streamlit as st

st.title("Minimal Streamlit App")
st.write("Hello from Domino!")
```

```bash app.sh theme={null}
#!/bin/bash

mkdir -p ~/.streamlit

cat << EOF > ~/.streamlit/config.toml
[browser]
gatherUsageStats = false

[server]
port = 8888
enableCORS = false
enableXsrfProtection = false
address = "0.0.0.0"
EOF

streamlit run app.py
```

If you set a route prefix for the App, read it from `DOMINO_RUN_HOST_PATH`.

### Example: chatbot

Domino's [Streamlit chatbot example](https://github.com/dominodatalab/docs_llm_app_example) sends prompts to large language models (LLMs) hosted on [Hugging Face](https://huggingface.co/inference-endpoints/dedicated) or to a [Domino endpoint](https://docs.domino.ai/6.3/platform-capabilities/features/model-deployment). To run it:

1. Add your Hugging Face access token as a [user environment variable](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/projects/project-settings.md#store-credentials-as-environment-variables) named `HUGGING_FACE_API_TOKEN`.
2. Install `streamlit`, `requests`, `pandas`, `transformers`, and `torch` in the Compute Environment.
3. Copy `chatbot.py` from the example into your Project. Set the endpoint's URL and access token as Project environment variables, such as `MODEL_ENDPOINT_URL` and `MODEL_ACCESS_TOKEN`, and read them in `chatbot.py`. [Select Domino endpoint authorization mode](https://docs.domino.ai/6.3/platform-capabilities/features/model-deployment/domino-endpoint-authorization.md) explains endpoint tokens.
4. Use the `app.sh` above with `streamlit run chatbot.py` as its last line.

## Flask

[Flask](https://flask.palletsprojects.com/) suits custom endpoints and bespoke web Apps. Flask works with Domino routing and identity headers without extra configuration.

```python app.py theme={null}
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello from Flask in Domino!"

if __name__ == '__main__':
    app.run(port=8888, host='0.0.0.0')
```

```bash app.sh theme={null}
#!/usr/bin/env bash
python app.py
```

### Structure a larger Flask App

For an App with templates and static files, make the App a Python package. The names `__init__.py`, `static`, and `templates` are required. Flask looks for static files and templates in those folders.

```text theme={null}
|-- app.sh
|-- run.py
|-- /flask_app
    |-- __init__.py
    |-- views.py
    |-- /static
    |-- /templates
```

`__init__.py` creates the App and wraps it so that the URLs Flask builds include the path Domino serves the App under:

```python flask_app/__init__.py theme={null}
from flask import Flask

class ReverseProxied(object):
  def __init__(self, app):
      self.app = app
  def __call__(self, environ, start_response):
      script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
      if script_name:
          environ['SCRIPT_NAME'] = script_name
          path_info = environ['PATH_INFO']
          if path_info.startswith(script_name):
              environ['PATH_INFO'] = path_info[len(script_name):]
      # Setting wsgi.url_scheme from Headers set by proxy before app
      scheme = environ.get('HTTP_X_SCHEME', 'https')
      if scheme:
        environ['wsgi.url_scheme'] = scheme
      # Setting HTTP_HOST from Headers set by proxy before app
      remote_host = environ.get('HTTP_X_FORWARDED_HOST', '')
      remote_port = environ.get('HTTP_X_FORWARDED_PORT', '')
      if remote_host and remote_port:
          environ['HTTP_HOST'] = f'{remote_host}:{remote_port}'
      return self.app(environ, start_response)

app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)
```

`views.py` holds the App's routes. `run.py` imports both, and `app.sh` starts Flask on the required host and port:

```python flask_app/views.py theme={null}
from flask import render_template
from flask_app import app

@app.route('/')
def index():
    return render_template('hello.html')
```

```html flask_app/templates/hello.html theme={null}
<!DOCTYPE html>
<html>
  <body>
    <h1>Hello from Flask in Domino</h1>
  </body>
</html>
```

```python run.py theme={null}
from flask_app import app
from flask_app import views

if __name__ == '__main__':
    app.run()
```

```bash app.sh theme={null}
#!/usr/bin/env bash
export FLASK_APP=run.py
python -m flask run --host=0.0.0.0 --port=8888
```

## Shiny

[Shiny](https://shiny.rstudio.com/) is R's framework for reactive web Apps. This two-file App draws the [Telephones by region](https://shiny.rstudio.com/gallery/telephones-by-region.html) chart from R's built-in `WorldPhones` data. Shiny requires the files to be named `server.R` and `ui.R`.

```r server.R theme={null}
library(datasets)

function(input, output) {
  output$phonePlot <- renderPlot({
    barplot(WorldPhones[,input$region]*1000,
            main=input$region,
            ylab="Number of Telephones",
            xlab="Year")
  })
}
```

```r ui.R theme={null}
library(datasets)

fluidPage(
  titlePanel("Telephones by region"),
  sidebarLayout(
    sidebarPanel(
      selectInput("region", "Region:",
                  choices=colnames(WorldPhones)),
      hr(),
      helpText("Data from AT&T (1961) The World's Telephones.")
    ),
    mainPanel(
      plotOutput("phonePlot")
    )
  )
)
```

```bash app.sh theme={null}
#!/usr/bin/env bash
R -e 'shiny::runApp("./", port=8888, host="0.0.0.0")'
```

`app.sh` is the entry point. Identity headers reach a Shiny App only through Shiny Server Pro. Autoscale a Shiny App to serve any number of viewers; see [R and Shiny](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/deploy.md#r-and-shiny).

## Rust

[Rust](https://www.rust-lang.org/) suits Apps where latency, throughput, or correctness matter more than iteration speed. A Rust App is a compiled web server, for example built with [axum](https://github.com/tokio-rs/axum). Build it once in a Workspace or Job with a pinned toolchain, keep the binary in `/mnt/artifacts` in a Git-based Project so each App version pins it with the commit, and have `app.sh` run the binary without compiling.

```rust src/main.rs theme={null}
use axum::{Router, routing::get};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let port = std::env::var("PORT").unwrap_or_else(|_| "8888".into());
    let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}")).await?;
    axum::serve(listener, Router::new().route("/", get(|| async { "Hello from Domino" }))).await
}
```

```bash app.sh theme={null}
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
export PORT=8888
exec /mnt/artifacts/dist/my-app/my-app
```

Keep every URL the browser follows relative, such as `href="static/app.css"` and `fetch("api/data")`. Where a value must be absolute, read the prefix from `DOMINO_RUN_HOST_PATH`. Build on the App's Hardware Tier, so the binary matches the node's CPU.

The **domino-rust-apps** [Domino Skill](https://docs.domino.ai/6.3/platform-capabilities/features/coding-assistants/domino-skills.md) does the rest. It installs the pinned toolchain in the Compute Environment, scaffolds the Project with lint and release profiles, builds dated binaries, writes an `app.sh` that checks the binary against the committed sources, and covers viewer identity, Postgres, and corporate certificates, proxies, and package mirrors.

## Static HTML

Python's built-in HTTP server serves `index.html` and any other static files in the Project. Select `app.py` as the App file.

```python app.py theme={null}
import http.server
import socketserver

PORT = 8888
Handler = http.server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", PORT), Handler)
print("serving at port", PORT)
httpd.serve_forever()
```

## Next steps

1. [Commit your changes](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/develop-and-test#commit-your-changes), because App Preview runs the latest commit.
2. [Run the App in App Preview](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/develop-and-test#preview-the-app) with your entry point as the App file.
3. [Publish](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/publish.md) a version, then [deploy](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/deploy.md) it.


## Related topics

- [Apps](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/index.md)
- [Develop and test Apps](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/develop-and-test/index.md)
- [Create and manage Extensions](https://docs.domino.ai/6.3/platform-capabilities/features/extensions/create-manage-extensions.md)
- [Deploy Apps to production](https://docs.domino.ai/6.3/platform-capabilities/core-concepts/apps/deploy.md)
