Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/app/reflex_docs/pages/docs/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
render_inline_markdown,
render_markdown,
)
from reflex_docs.pages.docs.metadata import truncate_meta_description
from reflex_docs.pages.docs.metadata import (
component_title_name,
truncate_meta_description,
)
from reflex_docs.templates.docpage import docpage, h2_comp


Expand Down Expand Up @@ -1038,6 +1041,7 @@ def links(current_page, ll_doc_exists, path):
description=description,
image=image,
source_path=actual_path,
component_name=component_title_name([c[1] for c in component_list[1:]]),
)
def out():
toc = get_docgen_toc(actual_path)
Expand Down Expand Up @@ -1090,6 +1094,7 @@ def out():
description=ll_description,
image=image,
source_path=ll_actual_path,
component_name=component_title_name([c[1] for c in ll_list[1:]]),
)
def ll():
ll_virtual = virtual_path.replace(".md", "-ll.md")
Expand Down
50 changes: 38 additions & 12 deletions docs/app/reflex_docs/pages/docs/library.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import reflex as rx
from reflex.utils.format import to_snake_case, to_title_case
from reflex.utils.format import to_snake_case

from reflex_docs.docgen_pipeline import render_markdown
from reflex_docs.templates.docpage import docpage, h1_comp, h2_comp, text_comp_2


def get_display_name(name: str) -> str:
normalized = to_snake_case(name)
if normalized == "html":
return "HTML"
if normalized == "svg":
return "SVG"
return to_title_case(normalized, sep=" ")

from reflex_docs.utils.library import get_display_name

HTML_COMPONENT_ORDER = {
"html": 0,
Expand Down Expand Up @@ -114,20 +106,54 @@ def generate_gallery(
)


LIBRARY_FAQ = """## How Reflex components compare

Like Streamlit widgets, Reflex components are written in Python, but Reflex gives you full control over layout and styling. Instead of rerunning the whole script on each interaction as Streamlit does, Reflex runs event handlers that update state, and only the parts of the UI that depend on that state re-render. NiceGUI and Gradio also build web UIs in Python. Reflex's library covers forms, layouts, overlays, data tables, a spreadsheet-style data editor, and charts, with enterprise AG Grid and AG Charts components for data-heavy apps.

## Frequently asked questions

### Can I use Reflex components without learning React?

Yes. Reflex components compile to React on the frontend, but you write everything in Python. You never touch JavaScript, JSX, or React hooks.

### Do Reflex components work with pandas DataFrames?

Yes. [`rx.data_table`](/docs/library/tables-and-data-grids/data-table/) takes a pandas DataFrame directly, with built-in search, sorting, and pagination. For editable grids, use [`rx.data_editor`](/docs/library/tables-and-data-grids/data-editor/) or the enterprise [AG Grid](/docs/enterprise/ag-grid/) component.

### Can I style Reflex components with custom CSS?

Yes. Every component accepts style props and a `class_name` prop. With the [Tailwind plugin](/docs/styling/tailwind/) enabled, you can pass Tailwind utility classes to `class_name`. For animations, keyframes, or other global styles, add [custom stylesheets](/docs/styling/custom-stylesheets/).

### Are Reflex components accessible?

Many core components are built on Radix UI primitives, which follow WAI-ARIA patterns and include keyboard navigation, focus management, and screen reader support.

### Can I wrap my own React components?

Yes. Reflex can wrap any React component from npm: declare its props and events in Python and Reflex generates the bindings. See the [wrapping React guide](/docs/wrapping-react/overview/).

### Do I need to install components separately?

No. All core components are included when you install Reflex. For enterprise components like AG Grid and AG Charts, install the `reflex-enterprise` package.
"""


@docpage(
set_path="/library/",
right_sidebar=True,
pseudo_right_bar=True,
page_title="Python UI Component Library · Reflex Docs",
)
def library():
return rx.box(
h1_comp(
text="Component Library",
),
text_comp_2(
text="Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page contains a list of all builtin components.",
text="Reflex provides 100+ UI components that work entirely in Python — no HTML, CSS, or JavaScript required. Build forms, tables, data grids, charts, and complete web interfaces using familiar Python syntax. Each component is fully documented with examples and integrates directly with your Python state.",
),
component_grid(),
rx.el.div(render_markdown(LIBRARY_FAQ), class_name="mt-8"),
rx.el.p(
"Connect your components to data and events with the ",
rx.el.a(
Expand Down
68 changes: 65 additions & 3 deletions docs/app/reflex_docs/pages/docs/metadata.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
"""SEO metadata helpers for docs routes."""

from collections.abc import Sequence
from os.path import commonprefix

from reflex.utils.format import to_snake_case

GENERIC_DESCRIPTION_TEMPLATE = "{subject}: documentation, examples, and reference for building Python web applications with Reflex."

# Frontmatter components documented under a different public constructor.
_PUBLIC_NAME_OVERRIDES = {
"rx.lucide.Icon": "rx.icon",
"rx.toast.provider": "rx.toast",
}

# Namespaces too broad to name a page on their own.
_GENERIC_NAMESPACES = {"", "rx", "rx.recharts"}

# Summaries for generated catalogs, reference pages, and pages without usable prose.
PAGE_DESCRIPTIONS = {
"library": "Browse Reflex UI components for forms, layouts, data display, charts, and media, with Python examples and prop references.",
"library": "Build web UIs in pure Python with 100+ Reflex components for forms, tables, data grids, charts, layouts, and media. No JavaScript required.",
"recipes": "Find reusable Reflex recipes for page layouts, content, and authentication, with examples you can adapt for your Python app.",
"api-reference/app": "Configure rx.App, register pages, and customize application behavior with the App class attributes and methods reference.",
"api-reference/component": "Explore the Component API for creating Reflex UI elements, managing props, and customizing component rendering and behavior.",
Expand Down Expand Up @@ -38,6 +52,44 @@
}


def component_title_name(components: Sequence[str]) -> str | None:
"""Return the public name a component page should lead its title with.

Frontmatter lists implementation names (``rx.recharts.AreaChart``,
``rx.dialog.root``); search titles need the name users call. Pages that
share one namespace use it (``rx.dialog``); otherwise the first,
primary component is used. HTML element collections get no name.

Args:
components: The component names from a doc's frontmatter.

Returns:
The public component name, or None when the page has none.
"""
names = list(dict.fromkeys(_public_component_name(c) for c in components))
if not names or any(name.startswith("rx.el.") for name in names):
return None
namespace = ".".join(commonprefix([name.split(".") for name in names]))
return names[0] if namespace in _GENERIC_NAMESPACES else namespace


def _public_component_name(component: str) -> str:
"""Map a frontmatter component reference to the name users call.

Args:
component: A frontmatter component reference.

Returns:
The public constructor name.
"""
if component in _PUBLIC_NAME_OVERRIDES:
return _PUBLIC_NAME_OVERRIDES[component]
module, _, name = component.rpartition(".")
if module == "rx.recharts":
return f"{module}.{to_snake_case(name)}"
return component.removesuffix(".root")


def truncate_meta_description(description: str, max_len: int = 155) -> str:
"""Return a search-snippet-safe meta description.

Expand All @@ -58,13 +110,20 @@ def truncate_meta_description(description: str, max_len: int = 155) -> str:
return f"{truncated}…"


def docs_metadata(path: str, title: str, description: str | None) -> tuple[str, str]:
def docs_metadata(
path: str,
title: str,
description: str | None,
component_name: str | None = None,
) -> tuple[str, str]:
"""Build distinct search snippets using the page's product hierarchy.

Args:
path: The app-relative documentation route.
title: The page heading.
description: A summary extracted from the document, when available.
component_name: The public component name a library page documents,
which leads the title in place of the breadcrumb.

Returns:
The search title and description.
Expand Down Expand Up @@ -101,4 +160,7 @@ def docs_metadata(path: str, title: str, description: str | None) -> tuple[str,
summary = (
description or fallback or GENERIC_DESCRIPTION_TEMPLATE.format(subject=subject)
)
return f"{subject} · Reflex Docs", truncate_meta_description(summary)
heading = (
f"{component_name}: Python {title} Component" if component_name else subject
)
return f"{heading} · Reflex Docs", truncate_meta_description(summary)
47 changes: 31 additions & 16 deletions docs/app/reflex_docs/pages/library_previews.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import reflex as rx
from reflex.utils.format import to_snake_case, to_title_case
from reflex.utils.format import to_snake_case
from reflex_site_shared.constants import REFLEX_ASSETS_CDN

from reflex_docs.docgen_pipeline import render_markdown
from reflex_docs.pages.docs.metadata import truncate_meta_description
from reflex_docs.templates.docpage import docpage, h1_comp, text_comp_2


def get_display_name(name: str) -> str:
normalized = to_snake_case(name)
if normalized == "html":
return "HTML"
if normalized == "svg":
return "SVG"
return to_title_case(normalized, sep=" ")

from reflex_docs.utils.library import get_display_name

HTML_COMPONENT_ORDER = {
"html": 0,
Expand Down Expand Up @@ -129,6 +120,28 @@ def get_component_list(component_type: str = "core"):
raise ValueError(f"Unknown component type: {component_type}")


CHARTS_GUIDE = """## Choosing a charting library

- **[Recharts](/docs/library/graphing/charts/)** (built in): declarative charts with a simple API for standard business charts like bar, line, area, and pie. Good for dashboards and reports.
- **[Plotly](/docs/library/graphing/other-charts/plotly/)** (`rx.plotly`): interactive scientific charts with zoom, pan, and hover, including 3D plots, contours, and statistical charts.
- **[Matplotlib](/docs/library/graphing/other-charts/pyplot/)** (`pyplot`): render any `matplotlib.pyplot` figure as a static image. Best when you already have Matplotlib code from notebooks or scripts.
- **AG Charts** (enterprise): high-performance charts with animations and advanced features. See the [AG Charts docs](/docs/enterprise/ag-chart/) for examples.
"""

# Markdown guides shown between a category's intro and its component cards.
CATEGORY_GUIDES = {
"Tables-And-Data-Grids": """## Which table component should I use?

- **`rx.table`**: a composable semantic table. Build headers, rows, and cells yourself for full control over structure and styling. Best for small datasets and custom layouts.
- **`rx.data_table`**: pass a pandas DataFrame or a list of rows and turn on search, sorting, and pagination with `search=True`, `sort=True`, and `pagination=True`. Read-only; best for displaying data from CSV files or database queries.
- **`rx.data_editor`**: a spreadsheet-like grid with cell selection and inline editing. Use it when users edit data directly, as in an admin panel or data entry form.
- **AG Grid** (enterprise): advanced filtering, column pivoting, row grouping, master-detail, tree data, and Excel export. See the [AG Grid docs](/docs/enterprise/ag-grid/) to get started.
""",
"Charts": CHARTS_GUIDE,
"Other-Charts": CHARTS_GUIDE,
}


def create_previews(
path: str,
description: str,
Expand All @@ -151,7 +164,7 @@ def page() -> rx.Component:

component_list = get_component_list(type)
if component_category == "State":
return rx.box(
return rx.el.div(
h1_comp(text="State"),
render_markdown(
"Reflex state connects your components to application data. "
Expand All @@ -175,15 +188,17 @@ def page() -> rx.Component:
),
class_name="docs-library flex min-w-0 flex-col w-full mb-10",
)
return rx.box(
rx.box(
guide = CATEGORY_GUIDES.get(component_category)
return rx.el.div(
rx.el.div(
h1_comp(text=get_display_name(component_category)),
text_comp_2(
text=description,
),
class_name="flex flex-col w-full",
),
rx.box(
*([rx.el.div(render_markdown(guide), class_name="mb-8")] if guide else []),
rx.el.div(
*[
component_card(
name=component[0],
Expand Down Expand Up @@ -259,7 +274,7 @@ def page() -> rx.Component:
},
"tables_and_data_grids": {
"path": "tables-and-data-grids",
"description": "Build tables and data grids in Python with Reflex. Includes a composable table, a searchable and sortable data table for pandas DataFrames, and an editable data grid — perfect for dashboards and data apps, all in pure Python.",
"description": "Build tables and data grids in Python with Reflex. Use rx.table for simple semantic tables, rx.data_table for searchable pandas DataFrames, rx.data_editor for spreadsheet-style editing, or the enterprise AG Grid component for advanced features like filtering, pivoting, and cell selection. All in pure Python.",
"component_category": "Tables-And-Data-Grids",
},
"typography": {
Expand Down
7 changes: 6 additions & 1 deletion docs/app/reflex_docs/templates/docpage/docpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ def docpage(
image: str | None = None,
source_path: str | None = None,
show_breadcrumb: bool = True,
component_name: str | None = None,
):
"""A template that most pages on the reflex.dev site should use.

Expand All @@ -392,6 +393,8 @@ def docpage(
"Edit this page" link. Defaults to the Python file defining the page.
show_breadcrumb: Whether to display the page breadcrumb. Mobile sidebar
access remains available when the breadcrumb is hidden.
component_name: The public component name a library page documents,
used to lead its search title.

Returns:
A wrapper function that returns the full webpage.
Expand Down Expand Up @@ -538,7 +541,9 @@ def wrapper(*args, **kwargs) -> rx.Component:

from reflex_docs.pages.docs.metadata import docs_metadata

seo_title, seo_description = docs_metadata(path, title, description)
seo_title, seo_description = docs_metadata(
path, title, description, component_name
)
if page_title:
seo_title = page_title

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import reflex as rx
from reflex.utils.format import to_snake_case, to_title_case

from ..state import SideBarItem
from reflex.utils.format import to_snake_case

from reflex_docs.utils.library import get_display_name

def get_display_name(name: str) -> str:
normalized = to_snake_case(name)
if normalized == "html":
return "HTML"
if normalized == "svg":
return "SVG"
return to_title_case(normalized, sep=" ")
from ..state import SideBarItem


def is_html_category(category: str) -> bool:
Expand Down
21 changes: 21 additions & 0 deletions docs/app/reflex_docs/utils/library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Naming helpers shared by the component library pages and sidebar."""

from reflex.utils.format import to_snake_case, to_title_case


def get_display_name(name: str) -> str:
"""Turn a library category or component key into a display label.

Args:
name: A key such as ``Data-Display`` or ``data_table``.

Returns:
The title-cased label, e.g. ``Data Display``.
"""
# Dashed keys like "Data-Display" snake-case to "data__display".
normalized = to_snake_case(name).replace("__", "_")
if normalized == "html":
return "HTML"
if normalized == "svg":
return "SVG"
return to_title_case(normalized, sep=" ")
Loading
Loading