Intro

While the .app() method is a great quick start for exploring data, building custom apps with querychat unlocks the full power of integrating natural language data exploration with custom visualizations, layouts, and interactivity.

querychat lets users ask questions of their data in plain language — filtering, sorting, summarizing, joining across tables, and creating visualizations — all without needing to write SQL or navigate complex filter UIs. You can use it as the primary exploration interface in a standalone app, or embed it alongside curated views in an existing dashboard to let users go deeper than the views you designed.

This is especially valuable when:

General pattern

Regardless of framework, building a custom querychat app follows the same pattern:

  1. Initialize a QueryChat instance with your data
  2. Place the chat UI in your layout (.sidebar() or .ui())
  3. Access query state (.df(), .sql(), .title()) to build reactive outputs
  4. Connect visualizations and tables to the filtered data

Each framework has its own way of handling reactivity and state updates, but the core querychat API is largely consistent across all of them.

Choosing a framework

querychat supports four Python web frameworks. Choose based on your needs:

Framework Best for Guide
Shiny Complex reactive apps, fine-grained control, Posit Connect deployment Build with Shiny
Streamlit Quick prototypes, simple apps, script-based development Build with Streamlit
Gradio ML demos, easy sharing, Hugging Face Spaces Build with Gradio
Dash Production enterprise apps, complex callbacks Build with Dash

API differences

While querychat provides a consistent interface across frameworks, each implementation follows its framework’s idioms for state management. The key difference is how you access query state:

Framework State access Example
Shiny Reactive callables qc.df()(), qc.sql()(), qc.title()()
Streamlit Direct methods (session state) qc.df(), qc.sql(), qc.title()
Gradio Methods with state dict qc.df(state), qc.sql(state), qc.title(state)
Dash Methods with state dict qc.df(state), qc.sql(state), qc.title(state)

Why the difference?

  • Shiny uses reactive programming where values are wrapped in reactive containers. You call .df() to get the reactive, then call it again (or use it in a reactive context) to get the value.
  • Streamlit manages state internally via st.session_state, so methods can access it directly without arguments.
  • Gradio and Dash use callback-based architectures where state is passed explicitly to callback functions, so you pass the state dict to accessors.

Each approach matches how developers typically work in that framework, making querychat feel native regardless of which framework you choose.