diff --git a/docs/app/assets/tailwind-theme.css b/docs/app/assets/tailwind-theme.css index 2b31897abfa..fbf4e63e176 100644 --- a/docs/app/assets/tailwind-theme.css +++ b/docs/app/assets/tailwind-theme.css @@ -1699,6 +1699,14 @@ /* padding: 0rem 0.125rem 0rem 0.125rem; */ } + /* Override Radix Code (rt-Code) accent coloring so inline code matches slate theme */ + code.rt-Code, + code.rt-Code.rt-variant-soft, + .rt-Code.rt-variant-soft { + color: var(--c-slate-11) !important; + background-color: var(--c-slate-3) !important; + } + .code-error-style { font-family: var(--font-jetbrains); font-size: 0.835rem; @@ -1803,6 +1811,44 @@ } } + .code-block button, + .code-block > button { + border: none !important; + border-width: 0 !important; + background: transparent !important; + background-color: transparent !important; + opacity: 0 !important; + transition: opacity 0.15s ease-out !important; + pointer-events: none; + display: inline-flex !important; + align-items: center !important; + gap: 0.375rem !important; + font-size: 0.8125rem !important; + font-weight: 500 !important; + color: var(--c-slate-11) !important; + top: 8px !important; + right: 12px !important; + padding: 6px 8px !important; + } + + .code-block button::after, + .code-block > button::after { + content: "Copy"; + } + + .code-block:hover button, + .code-block:hover > button, + .code-block button:focus-visible { + opacity: 1 !important; + pointer-events: auto; + } + + .code-block button:hover, + .code-block > button:hover { + background: var(--c-slate-3) !important; + background-color: var(--c-slate-3) !important; + } + .tab-style { color: var(--c-slate-9); @@ -1814,6 +1860,89 @@ letter-spacing: -0.01094rem; } + .pill-tab-list { + display: inline-flex !important; + gap: 2px !important; + padding: 4px !important; + background: var(--c-slate-3) !important; + border-radius: 10px !important; + border-bottom: none !important; + box-shadow: none !important; + width: fit-content !important; + max-width: 100%; + overflow-x: auto; + } + + .pill-tab-list::before, + .pill-tab-list::after { + display: none !important; + content: none !important; + } + + .pill-tab { + appearance: none !important; + background: transparent !important; + background-color: transparent !important; + border: none !important; + padding: 8px 16px !important; + font-size: 0.9375rem !important; + font-weight: 450 !important; + color: var(--c-slate-11) !important; + border-radius: 7px !important; + cursor: pointer; + white-space: nowrap; + transition: color 0.12s, background-color 0.12s, box-shadow 0.12s; + letter-spacing: 0 !important; + line-height: 1.25rem !important; + box-shadow: none !important; + } + + .pill-tab::before, + .pill-tab::after, + .pill-tab:hover::before, + .pill-tab:hover::after, + .pill-tab[data-state='active']::before, + .pill-tab[data-state='active']::after, + .pill-tab[data-state='active']:hover::before, + .pill-tab[data-state='active']:hover::after { + background: transparent !important; + background-color: transparent !important; + box-shadow: none !important; + content: none !important; + display: none !important; + } + + .pill-tab:hover { + color: var(--c-slate-11) !important; + background: transparent !important; + } + + .pill-tab .rt-BaseTabListTriggerInner, + .pill-tab:hover .rt-BaseTabListTriggerInner, + .pill-tab:focus-visible .rt-BaseTabListTriggerInner, + .pill-tab:focus-visible:hover .rt-BaseTabListTriggerInner { + background-color: transparent !important; + background: transparent !important; + } + + .pill-tab[data-state='active'], + .pill-tab[data-state='active']:hover { + color: var(--c-slate-12) !important; + font-weight: 500 !important; + background: var(--c-slate-1) !important; + background-color: var(--c-slate-1) !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08) !important; + } + + :where(.dark, .dark *) .pill-tab[data-state='active'] { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4) !important; + } + + .tab-style:hover:not([data-state='active'])::before { + background: transparent !important; + background-color: transparent !important; + } + .tab-style:hover { color: var(--c-slate-11); } diff --git a/docs/app/reflex_docs/docgen_pipeline.py b/docs/app/reflex_docs/docgen_pipeline.py index 9ee8eaf8720..ffe984db1e9 100644 --- a/docs/app/reflex_docs/docgen_pipeline.py +++ b/docs/app/reflex_docs/docgen_pipeline.py @@ -666,15 +666,19 @@ def _render_tabs(self, block: DirectiveBlock) -> rx.Component: rx.tabs.trigger( title, value=value, - class_name="tab-style font-base font-semibold text-[1.25rem]", + class_name="pill-tab", ) ) contents.append( - rx.tabs.content(self._render_children(body_blocks), value=value), + rx.tabs.content( + self._render_children(body_blocks), + value=value, + class_name="pt-6", + ), ) return rx.tabs.root( - rx.tabs.list(*triggers, class_name="mt-4"), + rx.tabs.list(*triggers, class_name="pill-tab-list mt-2"), *contents, default_value="tab1", ) @@ -702,29 +706,43 @@ def _render_definition(self, block: DirectiveBlock) -> rx.Component: def _render_section(self, block: DirectiveBlock) -> rx.Component: """Render a ``md section`` directive.""" - from reflex_ui_shared.styles.colors import c_color - sections = self._split_children_by_heading(block.children) - return rx.box( - rx.vstack( + return rx.el.div( + rx.el.div( *[ - rx.fragment( - rx.text( - rx.text.span(header, font_weight="bold"), - width="100%", + rx.el.div( + rx.el.div( + header, + style={ + "fontWeight": "600", + "color": "var(--c-slate-12)", + "fontSize": "1rem", + "lineHeight": "1.5", + }, ), - rx.box(self._render_children(body), width="100%"), + rx.el.div( + self._render_children(body), + style={"width": "100%"}, + ), + style={ + "display": "flex", + "flexDirection": "column", + "gap": "0.25rem", + "width": "100%", + }, ) for header, body in sections ], - text_align="left", - margin_y="1em", - width="100%", + style={ + "display": "flex", + "flexDirection": "column", + "gap": "1.25rem", + "width": "100%", + "paddingLeft": "1.5rem", + "borderLeft": "1.5px solid var(--c-slate-4)", + }, ), - border_left=f"1.5px {c_color('slate', 4)} solid", - padding_left="1em", - width="100%", - align_items="center", + style={"width": "100%", "margin": "1.5rem 0"}, ) diff --git a/docs/app/reflex_docs/templates/docpage/docpage.py b/docs/app/reflex_docs/templates/docpage/docpage.py index 90c886e9169..d9f0d50e93c 100644 --- a/docs/app/reflex_docs/templates/docpage/docpage.py +++ b/docs/app/reflex_docs/templates/docpage/docpage.py @@ -531,8 +531,8 @@ def wrapper(*args, **kwargs) -> rx.Component: class_name="lg:mt-0 h-auto", ), class_name=ui.cn( - "flex-1 h-auto mx-auto lg:max-w-[42rem] px-4 overflow-y-auto", - "lg:max-w-[56rem]" if not show_right_sidebar else "", + "flex-1 h-auto mx-auto lg:max-w-[52rem] px-4 overflow-y-auto", + "lg:max-w-[64rem]" if not show_right_sidebar else "", ), ), rx.box( @@ -611,7 +611,7 @@ def wrapper(*args, **kwargs) -> rx.Component: ), ), class_name=( - "w-[240px] h-screen sticky top-0 shrink-0 hidden xl:block" + "w-[240px] h-screen sticky top-0 shrink-0 hidden 2xl:block" ), ) if show_right_sidebar and not pseudo_right_bar diff --git a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py index 99455effa1c..a0a2149b23e 100644 --- a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py +++ b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py @@ -10,8 +10,8 @@ def get_sidebar_items_learn(): create_item( "Getting Started", children=[ - getting_started.introduction, getting_started.installation, + getting_started.introduction, getting_started.basics, getting_started.project_structure, getting_started.dashboard_tutorial, diff --git a/docs/app/reflex_docs/views/docs_navbar.py b/docs/app/reflex_docs/views/docs_navbar.py index 4718bde7ddf..e9bdf2fab0f 100644 --- a/docs/app/reflex_docs/views/docs_navbar.py +++ b/docs/app/reflex_docs/views/docs_navbar.py @@ -70,7 +70,7 @@ def menu_item(text: str, href: str, active_str: str = "") -> rx.Component: to=href, ), class_name=ui.cn( - "xl:flex hidden h-full items-center justify-center", + "md:flex hidden h-full items-center justify-center", rx.cond(active, active_cn, ""), ), custom_attrs={"role": "menuitem"}, @@ -111,7 +111,7 @@ def navigation_menu() -> rx.Component: ), ui.navigation_menu.item( navbar_sidebar_button(), - class_name="xl:hidden flex", + class_name="md:hidden flex", unstyled=True, custom_attrs={"role": "menuitem"}, ), diff --git a/docs/getting_started/basics.md b/docs/getting_started/basics.md index 7d6e7f26ae9..fa55f27282f 100644 --- a/docs/getting_started/basics.md +++ b/docs/getting_started/basics.md @@ -2,9 +2,9 @@ import reflex as rx ``` -# Reflex Basics +# Basics -This page gives an introduction to the most common concepts that you will use to build Reflex apps. +**~10 min** · An introduction to the most common concepts you'll use to build Reflex apps. ```md section # You will learn how to: @@ -18,13 +18,7 @@ This page gives an introduction to the most common concepts that you will use to - Create pages and navigate between them ``` -[Install](/docs/getting_started/installation) `reflex` with uv before continuing. - -```bash -uv add reflex -``` - -Import the `reflex` library to get started. +If you haven't yet, [install Reflex](/docs/getting_started/installation) before continuing. Every example below imports the library as `rx`: ```python import reflex as rx @@ -46,7 +40,7 @@ Components can be nested inside each other to create complex UIs. To nest components as children, pass them as positional arguments to the parent component. In the example below, the `rx.text` and `my_button` components are children of the `rx.box` component. ```python demo exec -def my_page(): +def my_container(): return rx.box( rx.text("This is a page"), # Reference components defined in other functions. @@ -54,7 +48,7 @@ def my_page(): ) ``` -You can also use any base HTML element through the [`rx.el`](/docs/library/other/html) namespace. This allows you to use standard HTML elements directly in your Reflex app when you need more control or when a specific component isn't available in the Reflex component library. +You can also use any base HTML element through the [rx.el](/docs/library/other/html) namespace. This allows you to use standard HTML elements directly in your Reflex app when you need more control or when a specific component isn't available in the Reflex component library. ```python demo exec def my_div(): @@ -142,7 +136,7 @@ Vars can be referenced in multiple components, and will automatically update whe So far, we've defined state vars but we haven't shown how to change them. All state changes are handled through functions in the state class, called [event handlers](/docs/events/events_overview). ```md alert -Event handlers are the ONLY way to change state in Reflex. +Event handlers are the **only** way to change state in Reflex. ``` Components have special props, such as `on_click`, called event triggers that can be used to make components interactive. Event triggers connect components to event handlers, which update the state. @@ -214,7 +208,7 @@ def text_input(): Make sure that the event handler has the same number of arguments as the event trigger, or an error will be raised. ``` -## Compile-time vs. runtime (IMPORTANT) +## Compile-time vs. runtime Before we dive deeper into state, it's important to understand the difference between compile-time and runtime in Reflex. @@ -314,7 +308,7 @@ In the next sections, we will show how to handle these cases. ## Conditional rendering -As mentioned above, you cannot use Python `if/else` statements with state vars in components. Instead, use the [`rx.cond`](/docs/components/conditional_rendering) function to conditionally render components. +As mentioned above, you cannot use Python `if/else` statements with state vars in components. Instead, use the [rx.cond](/docs/components/conditional_rendering) function to conditionally render components. ```python demo exec class LoginState(rx.State): @@ -338,7 +332,7 @@ def show_login(): ## Rendering lists -To iterate over a var that is a list, use the [`rx.foreach`](/docs/components/rendering_iterables) function to render a list of components. +To iterate over a var that is a list, use the [rx.foreach](/docs/components/rendering_iterables) function to render a list of components. Pass the list var and a function that returns a component as arguments to `rx.foreach`. @@ -398,13 +392,26 @@ def index(): return rx.text("Root Page") -rx.app = rx.App() +app = rx.App() app.add_page(index, route="/") ``` ## Next Steps -Now that you have a basic understanding of how Reflex works, the next step is to start coding your own apps. Try one of the following tutorials: +You've got the core pieces — components, state, events, compile-time vs. runtime. Time to build. + +```md alert info +# Build something real → + +- [Dashboard tutorial](/docs/getting_started/dashboard_tutorial) — a data app with tables, forms, and state. +- [Chatapp tutorial](/docs/getting_started/chatapp_tutorial) — streaming AI responses end-to-end. +- [Open-source templates](/docs/getting_started/open_source_templates) — full apps to fork. +``` -- [Dashboard Tutorial](/docs/getting_started/dashboard_tutorial) -- [Chatapp Tutorial](/docs/getting_started/chatapp_tutorial) +```md alert info +# Go deeper → + +- [Vars](/docs/vars/base_vars) and [var operations](/docs/vars/var-operations) — the full API. +- [Events](/docs/events/events_overview) and [pages](/docs/pages/overview) — routing, triggers, handlers. +- [How Reflex works](/docs/advanced_onboarding/how-reflex-works) — what runs where, and why. +``` diff --git a/docs/getting_started/dashboard_tutorial.md b/docs/getting_started/dashboard_tutorial.md index 757e6e731d1..0d453aed9f8 100644 --- a/docs/getting_started/dashboard_tutorial.md +++ b/docs/getting_started/dashboard_tutorial.md @@ -4,24 +4,22 @@ import reflex as rx # Tutorial: Data Dashboard -During this tutorial you will build a small data dashboard, where you can input data and it will be rendered in table and a graph. This tutorial does not assume any existing Reflex knowledge, but we do recommend checking out the quick [Basics Guide](/docs/getting_started/basics) first. +**~20 min hands-on** · Build a small data dashboard where users can input data that renders in a table and a graph. -The techniques you’ll learn in the tutorial are fundamental to building any Reflex app, and fully understanding it will give you a deep understanding of Reflex. +This tutorial does not assume any existing Reflex knowledge, but we do recommend checking out the quick [Basics Guide](/docs/getting_started/basics) first. The techniques you'll learn are fundamental to any Reflex app. This tutorial is divided into several sections: -- **Setup for the Tutorial**: A starting point to follow the tutorial -- **Overview**: The fundamentals of Reflex UI (components and props) -- **Showing Dynamic Data**: How to use State to render data that will change in your app. -- **Add Data to your App**: Using a Form to let a user add data to your app and introduce event handlers. -- **Plotting Data in a Graph**: How to use Reflex's graphing components. -- **Final Cleanup and Conclusion**: How to further customize your app and add some extra styling to it. +- **Setup**: Get your machine ready. +- **Overview**: The fundamentals of Reflex UI (components and props). +- **Showing dynamic data**: Use State to render data that changes. +- **Add data to your app**: Use a Form + event handlers. +- **Plotting data in a graph**: Reflex's graphing components. +- **Final cleanup** + **[Full app](#full-app)**: Customize and see the finished code. -### What are you building? +## What are you building? -In this tutorial, you are building an interactive data dashboard with Reflex. - -You can see what the finished app and code will look like here: +An interactive data dashboard: a table of users, a form to add more, and a bar chart that updates as data changes. Want to skip ahead? Jump to the [Full app](#full-app) at the bottom. ```python exec import dataclasses @@ -143,192 +141,13 @@ def graph5(): ) ``` -```python eval -rx.vstack( - add_customer_button5(), - rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Name"), - rx.table.column_header_cell("Email"), - rx.table.column_header_cell("Gender"), - ), - ), - rx.table.body( - rx.foreach(State5.users, show_user5), - ), - variant="surface", - size="3", - width="100%", - ), - graph5(), - align="center", - width="100%", - on_mouse_enter=State5.transform_data, - border_width="2px", - border_radius="10px", - padding="1em", -) -``` - -```python -import reflex as rx -from collections import Counter - - -@dataclasses.dataclass -class User: - """The user model.""" - - name: str - email: str - gender: str - - -class State(rx.State): - users: list[User] = [ - User(name="Danilo Sousa", email="danilo@example.com", gender="Male"), - User(name="Zahra Ambessa", email="zahra@example.com", gender="Female"), - ] - users_for_graph: list[dict] = [] - - def add_user(self, form_data: dict): - self.users.append(User(**form_data)) - self.transform_data() - - def transform_data(self): - """Transform user gender group data into a format suitable for visualization in graphs.""" - # Count users of each gender group - gender_counts = Counter(user.gender for user in self.users) - - # Transform into list of dict so it can be used in the graph - self.users_for_graph = [ - {"name": gender_group, "value": count} - for gender_group, count in gender_counts.items() - ] - - -def show_user(user: User): - """Show a user in a table row.""" - return rx.table.row( - rx.table.cell(user.name), - rx.table.cell(user.email), - rx.table.cell(user.gender), - style={"_hover": {"bg": rx.color("gray", 3)}}, - align="center", - ) - - -def add_customer_button() -> rx.Component: - return rx.dialog.root( - rx.dialog.trigger( - rx.button( - rx.icon("plus", size=26), - rx.text("Add User", size="4"), - ), - ), - rx.dialog.content( - rx.dialog.title( - "Add New User", - ), - rx.dialog.description( - "Fill the form with the user's info", - ), - rx.form( - rx.flex( - rx.input(placeholder="User Name", name="name", required=True), - rx.input( - placeholder="user@reflex.dev", - name="email", - ), - rx.select( - ["Male", "Female"], - placeholder="male", - name="gender", - ), - rx.flex( - rx.dialog.close( - rx.button( - "Cancel", - variant="soft", - color_scheme="gray", - ), - ), - rx.dialog.close( - rx.button("Submit", type="submit"), - ), - spacing="3", - justify="end", - ), - direction="column", - spacing="4", - ), - on_submit=State.add_user, - reset_on_submit=False, - ), - max_width="450px", - ), - ) - - -def graph(): - return rx.recharts.bar_chart( - rx.recharts.bar( - data_key="value", - stroke=rx.color("accent", 9), - fill=rx.color("accent", 8), - ), - rx.recharts.x_axis(data_key="name"), - rx.recharts.y_axis(), - data=State.users_for_graph, - width="100%", - height=250, - ) - - -def index() -> rx.Component: - return rx.vstack( - add_customer_button(), - rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Name"), - rx.table.column_header_cell("Email"), - rx.table.column_header_cell("Gender"), - ), - ), - rx.table.body( - rx.foreach(State.users, show_user), - ), - variant="surface", - size="3", - width="100%", - ), - graph(), - align="center", - width="100%", - ) - - -app = rx.App( - theme=rx.theme(radius="full", accent_color="grass"), -) - -app.add_page( - index, - title="Customer Data App", - description="A simple app to manage customer data.", - on_load=State.transform_data, -) -``` - -Don't worry if you don't understand the code above, in this tutorial we are going to walk you through the whole thing step by step. - ## Setup for the tutorial -Check out the [installation docs](/docs/getting_started/installation) to get Reflex set up on your machine. Follow these to create a folder called `dashboard_tutorial`, which you will `cd` into, then run `uv init` and `uv add reflex`. - -We will choose template `0` when we run `uv run reflex init` to get the blank template. Finally run `uv run reflex run` to start the app and confirm everything is set up correctly. +1. [Install Reflex](/docs/getting_started/installation) if you haven't already. +2. Create a folder called `dashboard_tutorial` and `cd` into it. +3. Run `uv init` and `uv add reflex`. +4. Run `uv run reflex init` and choose template `0` (the blank template). +5. Run `uv run reflex run` to start the app and confirm everything works. ## Overview @@ -363,7 +182,12 @@ app.add_page(index) This code will render a page with the text "Hello World!" when you run your app like below: ```python eval -rx.text("Hello World!", border_width="2px", border_radius="10px", padding="1em") +rx.box( + rx.text("Hello World!"), + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", +) ``` ```md alert info @@ -375,29 +199,31 @@ For the rest of the tutorial the `app=rx.App()` and `app.add_page` will be impli Let's create a new component that will render a table. We will use the `table` component to do this. The `table` component has a `root`, which takes in a `header` and a `body`, which in turn take in `row` components. The `row` component takes in `cell` components which are the actual data that will be displayed in the table. ```python eval -rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Name"), - rx.table.column_header_cell("Email"), - rx.table.column_header_cell("Gender"), - ), - ), - rx.table.body( - rx.table.row( - rx.table.cell("Danilo Sousa"), - rx.table.cell("danilo@example.com"), - rx.table.cell("Male"), +rx.box( + rx.table.root( + rx.table.header( + rx.table.row( + rx.table.column_header_cell("Name"), + rx.table.column_header_cell("Email"), + rx.table.column_header_cell("Gender"), + ), ), - rx.table.row( - rx.table.cell("Zahra Ambessa"), - rx.table.cell("zahra@example.com"), - rx.table.cell("Female"), + rx.table.body( + rx.table.row( + rx.table.cell("Danilo Sousa"), + rx.table.cell("danilo@example.com"), + rx.table.cell("Male"), + ), + rx.table.row( + rx.table.cell("Zahra Ambessa"), + rx.table.cell("zahra@example.com"), + rx.table.cell("Female"), + ), ), ), - border_width="2px", - border_radius="10px", - padding="1em", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -431,31 +257,33 @@ Components in Reflex have `props`, which can be used to customize the component The `rx.table.root` component has for example the `variant` and `size` props, which customize the table as seen below. ```python eval -rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Name"), - rx.table.column_header_cell("Email"), - rx.table.column_header_cell("Gender"), - ), - ), - rx.table.body( - rx.table.row( - rx.table.cell("Danilo Sousa"), - rx.table.cell("danilo@example.com"), - rx.table.cell("Male"), +rx.box( + rx.table.root( + rx.table.header( + rx.table.row( + rx.table.column_header_cell("Name"), + rx.table.column_header_cell("Email"), + rx.table.column_header_cell("Gender"), + ), ), - rx.table.row( - rx.table.cell("Zahra Ambessa"), - rx.table.cell("zahra@example.com"), - rx.table.cell("Female"), + rx.table.body( + rx.table.row( + rx.table.cell("Danilo Sousa"), + rx.table.cell("danilo@example.com"), + rx.table.cell("Male"), + ), + rx.table.row( + rx.table.cell("Zahra Ambessa"), + rx.table.cell("zahra@example.com"), + rx.table.cell("Female"), + ), ), + variant="surface", + size="3", ), - variant="surface", - size="3", - border_width="2px", - border_radius="10px", - padding="1em", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -509,7 +337,7 @@ To iterate over a state var that is a list, we use the [`rx.foreach`](/docs/comp ```md alert info # Why can we not just splat this in a `for` loop -You might be wondering why a `foreach` is even needed to render this state variable and why we cannot just splat a `for` loop. Check out this [documentation]() to learn why. +You might be wondering why a `foreach` is even needed to render this state variable and why we cannot just splat a `for` loop. Check out this [documentation](/docs/getting_started/basics#compile-time-vs.-runtime) to learn why. ``` Here the render function is `show_user` which takes in a single user and returns a `table.row` component that displays the users name, email and gender. @@ -532,22 +360,24 @@ def show_user1(person: list): ``` ```python eval -rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Name"), - rx.table.column_header_cell("Email"), - rx.table.column_header_cell("Gender"), +rx.box( + rx.table.root( + rx.table.header( + rx.table.row( + rx.table.column_header_cell("Name"), + rx.table.column_header_cell("Email"), + rx.table.column_header_cell("Gender"), + ), ), + rx.table.body( + rx.foreach(State1.users, show_user1), + ), + variant="surface", + size="3", ), - rx.table.body( - rx.foreach(State1.users, show_user1), - ), - variant="surface", - size="3", - border_width="2px", - border_radius="10px", - padding="1em", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -627,22 +457,24 @@ def show_user2(user: User): ``` ```python eval -rx.table.root( - rx.table.header( - rx.table.row( - rx.table.column_header_cell("Name"), - rx.table.column_header_cell("Email"), - rx.table.column_header_cell("Gender"), +rx.box( + rx.table.root( + rx.table.header( + rx.table.row( + rx.table.column_header_cell("Name"), + rx.table.column_header_cell("Email"), + rx.table.column_header_cell("Gender"), + ), ), + rx.table.body( + rx.foreach(State2.users, show_user2), + ), + variant="surface", + size="3", ), - rx.table.body( - rx.foreach(State2.users, show_user2), - ), - variant="surface", - size="3", - border_width="2px", - border_radius="10px", - padding="1em", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -835,9 +667,10 @@ rx.vstack( variant="surface", size="3", ), - border_width="2px", - border_radius="10px", - padding="1em", + spacing="4", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -1061,9 +894,10 @@ rx.vstack( variant="surface", size="3", ), - border_width="2px", - border_radius="10px", - padding="1em", + spacing="4", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -1345,9 +1179,10 @@ rx.vstack( size="3", ), graph(), - border_width="2px", - border_radius="10px", - padding="1em", + spacing="4", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -1508,9 +1343,10 @@ rx.vstack( ), graph(), on_mouse_enter=State4.transform_data, - border_width="2px", - border_radius="10px", - padding="1em", + spacing="4", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -1543,9 +1379,9 @@ app = rx.App( Unfortunately in this tutorial here we cannot actually apply this to the live example on the page, but if you copy and paste the code below into a reflex app locally you can see it in action. -## Conclusion +## Full app -Finally let's make some final styling updates to our app. We will add some hover styling to the table rows and center the table inside the `show_user` with `style=\{"_hover": \{"bg": rx.color("gray", 3)}}, align="center"`. +Finally let's make some styling updates. We will add hover styling to the table rows and center the table inside `show_user` with `style={"_hover": {"bg": rx.color("gray", 3)}}, align="center"`. In addition, we will add some `width="100%"` and `align="center"` to the `index()` component to center the items on the page and ensure they stretch the full width of the page. @@ -1573,9 +1409,10 @@ rx.vstack( align="center", width="100%", on_mouse_enter=State5.transform_data, - border_width="2px", - border_radius="10px", - padding="1em", + spacing="4", + border=f"1px solid {rx.color('slate', 5)}", + border_radius="12px", + padding="2em", ) ``` @@ -1730,19 +1567,16 @@ app.add_page( ) ``` -And that is it for your first dashboard tutorial. In this tutorial we have created - -- a table to display user data -- a form to add new users to the table -- a dialog to showcase the form -- a graph to visualize the user data +## Recap -In addition to the above we have we have +You built: -- explored state to allow you to show dynamic data that changes over time -- explored events to allow you to make your app interactive and respond to user actions -- added styling to the app to make it look better +- A table that displays user data. +- A form (inside a dialog) to add new users. +- A bar chart that visualizes the distribution. -## Advanced Section (Hooking this up to a Database) +Along the way you learned: -Coming Soon! +- **State** — how to store data that changes over time. +- **Events** — how to respond to user actions and update the UI. +- **Styling** — tweaking theme, layout, and hover states. diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md index ac981feefd6..7cb80ec23eb 100644 --- a/docs/getting_started/installation.md +++ b/docs/getting_started/installation.md @@ -1,35 +1,16 @@ -```python exec -import reflex as rx - -app_name = "my_app_name" -default_url = "http://localhost:3000" -``` - -# Installation - -Reflex requires Python 3.10+. - -```md video https://youtube.com/embed/ITOZkzjtjUA?start=758&end=1206 -# Video: Installation -``` +~3 minutes · Requires Python 3.10+. ## Virtual Environment -We **highly recommend** creating a virtual environment for your project. +We recommend [uv](https://docs.astral.sh/uv/) as the default; [venv](https://docs.python.org/3/library/venv.html), [conda](https://conda.io/), and [poetry](https://python-poetry.org/) are alternatives. -[uv](https://docs.astral.sh/uv/) is the recommended modern option. [venv](https://docs.python.org/3/library/venv.html), [conda](https://conda.io/) and [poetry](https://python-poetry.org/) are some alternatives. - -# Install Reflex on your system +## Install Reflex on your system `````md tabs ## macOS/Linux -We will go with [uv](https://docs.astral.sh/uv/) here. - -### Prerequisites - -#### Install uv +### Install uv ```bash curl -LsSf https://astral.sh/uv/install.sh | sh @@ -39,51 +20,30 @@ After installation, restart your terminal or run `source ~/.bashrc` (or `source Alternatively, install via [Homebrew, PyPI, or other methods](https://docs.astral.sh/uv/getting-started/installation/). -**macOS (Apple Silicon) users:** Install [Rosetta 2](https://support.apple.com/en-us/HT211861). Run this command: - -`/usr/sbin/softwareupdate --install-rosetta --agree-to-license` - -### Create the project directory +```md alert info +# macOS (Apple Silicon) users: install Rosetta 2 -Replace `{app_name}` with your project name. Switch to the new directory. - -```bash -mkdir {app_name} -cd {app_name} +Run `/usr/sbin/softwareupdate --install-rosetta --agree-to-license`. See [Apple's instructions](https://support.apple.com/en-us/HT211861) for details. ``` -### Initialize uv project - -```bash -uv init -``` +### Set up the Reflex project -### Add Reflex to the project +Replace `` with your project name, then switch into the new directory. ```bash +mkdir +cd +uv init uv add reflex -``` - -### Initialize the Reflex project - -```bash uv run reflex init ``` ## Windows -For Windows users, we recommend using [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/about) for optimal performance. +For Windows users, we recommend [WSL](https://learn.microsoft.com/en-us/windows/wsl/about) for optimal performance — WSL users should follow the macOS/Linux tab; the rest of this section covers native Windows. -**WSL users:** Refer to the macOS/Linux instructions above. - -For the rest of this section we will work with native Windows (non-WSL). - -We will go with [uv](https://docs.astral.sh/uv/) here. - -### Prerequisites - -#### Install uv +### Install uv ```powershell powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" @@ -93,30 +53,15 @@ After installation, restart your terminal (PowerShell or Command Prompt). Alternatively, install via [WinGet, Scoop, or other methods](https://docs.astral.sh/uv/getting-started/installation/). -### Create the project directory +### Set up the Reflex project -Replace `{app_name}` with your project name. Switch to the new directory. +Replace `` with your project name, then switch into the new directory. -```bash -mkdir {app_name} -cd {app_name} -``` - -### Initialize uv project - -```bash +```powershell +mkdir +cd uv init -``` - -### Add Reflex to the project - -```bash uv add reflex -``` - -### Initialize the Reflex project - -```bash uv run reflex init ``` @@ -127,6 +72,7 @@ Bun requires runtime components of Visual C++ libraries to run on Windows. This ``` ````` + Running `uv run reflex init` will return the option to start with a blank Reflex app, premade templates built by the Reflex team, or to try our [AI builder](https://build.reflex.dev/). ```bash @@ -139,7 +85,7 @@ Get started with a template: Which template would you like to use? (0): ``` -From here select an option. +If this is your first time, pick **(0) A blank Reflex app** — the rest of the docs assume you started there. ## Run the App @@ -149,12 +95,16 @@ Run it in development mode: uv run reflex run ``` -Your app runs at [http://localhost:3000](http://localhost:3000). +Your app runs at [http://localhost:3000](http://localhost:3000). Reflex _hot reloads_ any code changes in real time — your edits show up automatically. -Reflex prints logs to the terminal. To increase log verbosity to help with debugging, use the `--loglevel` flag: +For troubleshooting, increase log verbosity with the `--loglevel` flag: ```bash uv run reflex run --loglevel debug ``` -Reflex will _hot reload_ any code changes in real time when running in development mode. Your code edits will show up on [http://localhost:3000](http://localhost:3000) automatically. +```md alert info +# Next: Build your first app → + +Reflex is installed. The [Introduction](/docs/getting-started/introduction) walks through a working counter app in pure Python — the shortest path from "it runs" to "I understand it." +``` diff --git a/docs/getting_started/introduction.md b/docs/getting_started/introduction.md index ca5c5e24ad8..fdb325b92ec 100644 --- a/docs/getting_started/introduction.md +++ b/docs/getting_started/introduction.md @@ -2,35 +2,31 @@ import reflex as rx ``` -# Introduction - -**Reflex** is an open-source framework for quickly building beautiful, interactive web applications in **pure Python**. +**~5 min** · **Reflex** lets you build and deploy full-stack web apps — frontend, backend, and database — in **pure Python**. No JavaScript, no separate API, no context switching. ## Goals ```md section ### Pure Python -Use Python for everything. Don't worry about learning a new language. +Write your entire app — frontend, backend, database — in Python. No need to learn another language. ### Easy to Learn -Build and share your first app in minutes. No web development experience required. +Ship your first app in minutes. No web development experience required. ### Full Flexibility -Remain as flexible as traditional web frameworks. Reflex is easy to use, yet allows for advanced use cases. - -Build anything from small data science apps to large, multi-page websites. **This entire site was built and deployed with Reflex!** +Build anything from small data apps to large multi-page websites. **This entire site was built and deployed with Reflex.** ### Batteries Included -No need to reach for a bunch of different tools. Reflex handles the user interface, server-side logic, and deployment of your app. +One tool covers it all: UI, server-side logic, and deployment. ``` -## An example: Make it count +## Build a counter -Here, we go over a simple counter app that lets the user count up or down. +We'll build a counter app that lets the user count up or down. In ~20 lines of Python you'll touch the three core pieces of every Reflex app: **state**, **event handlers**, and **components**. ```python exec class CounterExampleState(rx.State): @@ -60,17 +56,14 @@ class IntroTabsState(rx.State): def tabs(): return rx.tabs.root( rx.tabs.list( - rx.tabs.trigger("Frontend", value="tab1", class_name="tab-style"), - rx.tabs.trigger("Backend", value="tab2", class_name="tab-style"), - rx.tabs.trigger("Page", value="tab3", class_name="tab-style"), + rx.tabs.trigger("Frontend", value="tab1", class_name="pill-tab"), + rx.tabs.trigger("Backend", value="tab2", class_name="pill-tab"), + rx.tabs.trigger("Page", value="tab3", class_name="pill-tab"), + class_name="pill-tab-list", ), rx.tabs.content( rx.markdown( - """The frontend is built declaratively using Reflex components. Components are compiled down to JS and served to the users browser, therefore: - -- Only use Reflex components, vars, and var operations when building your UI. Any other logic should be put in your `State` (backend). - -- Use `rx.cond` and `rx.foreach` (replaces if statements and for loops), for creating dynamic UIs. + """The frontend is built declaratively with Reflex components, which compile to JS and run in the browser. Use `rx.cond` and `rx.foreach` instead of `if` and `for` for dynamic UIs. Any non-UI logic belongs in `State`. """, ), value="tab1", @@ -86,9 +79,7 @@ def tabs(): ), rx.tabs.content( rx.markdown( - """Each page is a Python function that returns a Reflex component. You can define multiple pages and navigate between them, see the [Routing](/docs/pages/overview) section for more information. - -- Start with a single page and scale to 100s of pages. + """Each page is a Python function returning a Reflex component. Add as many as you want and link between them — see [Routing](/docs/pages/overview) for details. """, ), value="tab3", @@ -143,12 +134,12 @@ rx.box( self.count -= 1""", background=rx.cond( IntroTabsState.value == "tab2", - "var(--c-violet-3) !important", + "var(--c-slate-3) !important", "transparent", ), border=rx.cond( IntroTabsState.value == "tab2", - "1px solid var(--c-violet-5)", + "1px solid var(--c-slate-5)", "none !important", ), class_name="code-block", @@ -171,12 +162,12 @@ rx.box( )""", border=rx.cond( IntroTabsState.value == "tab1", - "1px solid var(--c-violet-5)", + "1px solid var(--c-slate-5)", "none !important", ), background=rx.cond( IntroTabsState.value == "tab1", - "var(--c-violet-3) !important", + "var(--c-slate-3) !important", "transparent", ), class_name="code-block", @@ -186,12 +177,12 @@ rx.box( app.add_page(index)""", background=rx.cond( IntroTabsState.value == "tab3", - "var(--c-violet-3) !important", + "var(--c-slate-3) !important", "transparent", ), border=rx.cond( IntroTabsState.value == "tab3", - "1px solid var(--c-violet-5)", + "1px solid var(--c-slate-5)", "none !important", ), class_name="code-block", @@ -210,7 +201,7 @@ Let's break this example down. import reflex as rx ``` -We begin by importing the `reflex` package (aliased to `rx`). We reference Reflex objects as `rx.*` by convention. +Import Reflex as `rx`. All Reflex objects are accessed as `rx.*`. ### State @@ -219,9 +210,7 @@ class State(rx.State): count: int = 0 ``` -The state defines all the variables (called **[vars](/docs/vars/base_vars)**) in an app that can change, as well as the functions (called **[event_handlers](#event-handlers)**) that change them. - -Here our state has a single var, `count`, which holds the current value of the counter. We initialize it to `0`. +State holds the app's mutable data. Variables declared here are called **[vars](/docs/vars/base_vars)**. Our counter has one: `count`, starting at `0`. ### Event Handlers @@ -236,13 +225,7 @@ def decrement(self): self.count -= 1 ``` -Within the state, we define functions, called **event handlers**, that change the state vars. - -Event handlers are the only way that we can modify the state in Reflex. -They can be called in response to user actions, such as clicking a button or typing in a text box. -These actions are called **events**. - -Our counter app has two event handlers, `increment` and `decrement`. +**Event handlers** are the only way to modify state. They're triggered by user actions (clicks, typing, etc.) — those actions are called **events**. Our counter has two handlers: `increment` and `decrement`. ### User Interface (UI) @@ -264,70 +247,44 @@ def index(): ) ``` -This function defines the app's user interface. +The UI is built from components (`rx.hstack`, `rx.button`, `rx.heading`) that can be nested and styled with CSS or [Tailwind](/docs/styling/tailwind). Reflex ships with [50+ built-in components](/docs/library), and you can [wrap any React component](/docs/wrapping-react/overview). -We use different components such as `rx.hstack`, `rx.button`, and `rx.heading` to build the frontend. Components can be nested to create complex layouts, and can be styled using the full power of CSS or [Tailwind CSS](/docs/styling/tailwind). +Components reference state vars (`rx.heading(State.count, …)`) and reactively re-render when state changes. Event triggers (`on_click=State.decrement`) wire UI to handlers. -Reflex comes with [50+ built-in components](/docs/library) to help you get started. -We are actively adding more components. Also, it's easy to [wrap your own React components](/docs/wrapping-react/overview). +The sequence goes like this: -```python -(rx.heading(State.count, font_size="2em"),) -``` - -Components can reference the app's state vars. -The `rx.heading` component displays the current value of the counter by referencing `State.count`. -All components that reference state will reactively update whenever the state changes. - -```python -( - rx.button( - "Decrement", - color_scheme="ruby", - on_click=State.decrement, - ), -) -``` - -Components interact with the state by binding events triggers to event handlers. -For example, `on_click` is an event that is triggered when a user clicks a component. - -The first button in our app binds its `on_click` event to the `State.decrement` event handler. Similarly the second button binds `on_click` to `State.increment`. - -In other words, the sequence goes like this: - -- User clicks "increment" on the UI. -- `on_click` event is triggered. -- Event handler `State.increment` is called. -- `State.count` is incremented. -- UI updates to reflect the new value of `State.count`. +1. User clicks "Increment". +2. `on_click` fires. +3. `State.increment` runs on the server. +4. `State.count` is updated. +5. UI re-renders with the new value. ### Add pages -Next we define our app and add the counter component to the base route. - ```python app = rx.App() app.add_page(index) ``` -## Next Steps - -🎉 And that's it! +Create the app and register the page at the base route. -We've created a simple, yet fully interactive web app in pure Python. +## Next Steps -By continuing with our documentation, you will learn how to build awesome apps with Reflex. Use the sidebar to navigate through the sections, or search (`Ctrl+K` or `Cmd+K`) to quickly find a page. +🎉 You've built a fully interactive web app in pure Python. -For a glimpse of the possibilities, check out these resources: +```md alert info +# Keep learning → -- For a more real-world example, check out either the [dashboard tutorial](/docs/getting_started/dashboard_tutorial) or the [chatapp tutorial](/docs/getting_started/chatapp_tutorial). -- Check out our open-source [templates](/docs/getting_started/open_source_templates)! -- We have an AI Builder that can generate full Reflex apps or help with your existing app! Check it out at [Reflex Build](https://build.reflex.dev/)! -- Deploy your app with a single command using [Reflex Cloud](https://reflex.dev/docs/hosting/deploy-quick-start/)! +- [Dashboard tutorial](/docs/getting_started/dashboard_tutorial) — build a real data app. +- [Chatapp tutorial](/docs/getting_started/chatapp_tutorial) — wire up streaming AI responses. +- [How Reflex works](/docs/advanced_onboarding/how-reflex-works) — what happens under the hood. +``` -If you want to learn more about how Reflex works, check out the [How Reflex Works](/docs/advanced_onboarding/how-reflex-works) section. +```md alert info +# Ship faster with AI → -## Join our Community +- [Reflex Build](https://build.reflex.dev/) — generate a full app from a prompt. +- [Reflex Cloud](https://reflex.dev/docs/hosting/deploy-quick-start/) — one-command deploy. +``` -If you have questions about anything related to Reflex, you're always welcome to ask our community on [GitHub Discussions](https://github.com/orgs/reflex-dev/discussions), [Discord](https://discord.gg/T5WSbC2YtQ), [Forum](https://forum.reflex.dev), and [X](https://twitter.com/getreflex). +Browse our [open-source templates](/docs/getting_started/open_source_templates), or press `Cmd+K` / `Ctrl+K` to search the docs. diff --git a/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/headings.py b/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/headings.py index c75b145b940..de03f23a4f6 100644 --- a/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/headings.py +++ b/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/headings.py @@ -136,7 +136,7 @@ def create( underline="none", href=href, on_click=lambda: rx.set_clipboard(href), - class_name="flex flex-row items-center gap-6 hover:!text-violet-11 cursor-pointer mb-6 transition-colors group text-m-slate-12 dark:text-m-slate-3 ", + class_name="flex flex-row items-center gap-2 hover:!text-violet-11 cursor-pointer mb-3 transition-colors group text-m-slate-12 dark:text-m-slate-3 ", ) @@ -153,7 +153,7 @@ def h1_comp(text: str) -> rx.Component: return h_comp_common( text=text, heading="h1", - class_name="lg:text-5xl text-3xl font-[525]", + class_name="lg:text-4xl text-3xl font-semibold", ) @@ -167,7 +167,7 @@ def h1_comp_xd(text: str) -> rx.Component: return h_comp_common( text=text, heading="h1", - class_name="lg:text-5xl text-3xl font-[525]", + class_name="lg:text-4xl text-3xl font-semibold", ) @@ -182,7 +182,7 @@ def h2_comp(text: str) -> rx.Component: text=text, heading="h2", mt="8", - class_name="lg:text-4xl text-2xl font-[525]", + class_name="lg:text-3xl text-2xl font-semibold", ) @@ -197,7 +197,7 @@ def h2_comp_xd(text: str) -> rx.Component: text=text, heading="h2", mt="8", - class_name="lg:text-3xl text-2xl font-[525]", + class_name="lg:text-2xl text-xl font-semibold", ) @@ -212,7 +212,7 @@ def h3_comp(text: str) -> rx.Component: text=text, heading="h3", mt="4", - class_name="lg:text-2xl text-xl font-[525]", + class_name="lg:text-xl text-lg font-semibold", ) @@ -227,7 +227,7 @@ def h3_comp_xd(text: str) -> rx.Component: text=text, heading="h3", mt="4", - class_name="lg:text-2xl text-lg font-[525]", + class_name="lg:text-xl text-lg font-semibold", ) @@ -242,7 +242,7 @@ def h4_comp(text: str) -> rx.Component: text=text, heading="h4", mt="2", - class_name="lg:text-xl text-lg font-[525]", + class_name="lg:text-base text-base font-semibold", ) @@ -257,7 +257,7 @@ def h4_comp_xd(text: str) -> rx.Component: text=text, heading="h4", mt="2", - class_name="lg:text-xl text-lg font-[525]", + class_name="lg:text-base text-base font-semibold", ) diff --git a/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/typography.py b/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/typography.py index ae32b462857..e7deacd833b 100644 --- a/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/typography.py +++ b/packages/reflex-ui-shared/src/reflex_ui_shared/components/blocks/typography.py @@ -39,7 +39,7 @@ def text_comp(text: rx.Var[str]) -> rx.Component: Returns: The component. """ - return rx.text(text, class_name="font-[475] text-secondary-11 mb-4 leading-7") + return rx.text(text, class_name="font-normal text-secondary-11 mb-4 leading-7") @rx.memo @@ -51,7 +51,7 @@ def text_comp_2(text: rx.Var[str]) -> rx.Component: """ return rx.text( text, - class_name="font-[475] text-secondary-11 max-w-[80%] mb-10", + class_name="font-normal text-secondary-11 max-w-[80%] mb-10", ) @@ -62,7 +62,7 @@ def list_comp(text: rx.Var[str]) -> rx.Component: Returns: The component. """ - return rx.list_item(text, class_name="font-[475] text-secondary-11 mb-4") + return rx.list_item(text, class_name="font-normal text-secondary-11 mb-4") @rx.memo