Back to Reflex

Introduction

docs/getting_started/introduction.md

0.9.2a27.7 KB
Original Source
python
import reflex as rx

Introduction

~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
### Pure Python

Write your entire app — frontend, backend, database — in Python. No need to learn another language.

### Easy to Learn

Ship your first app in minutes. No web development experience required.

### Full Flexibility

Build anything from small data apps to large multi-page websites. **This entire site was built and deployed with Reflex.**

### Batteries Included

One tool covers it all: UI, server-side logic, and deployment.

Build a counter

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
class CounterExampleState(rx.State):
    count: int = 0

    @rx.event
    def increment(self):
        self.count += 1

    @rx.event
    def decrement(self):
        self.count -= 1


class IntroTabsState(rx.State):
    """The app state."""

    value: str = "tab1"
    tab_selected: str = ""

    @rx.event
    def change_value(self, val: str):
        self.tab_selected = f"{val} clicked!"
        self.value = val


def counter_code_section(code: str, tab: str) -> rx.Component:
    active = IntroTabsState.value == tab
    return rx.box(
        rx.code_block(
            code,
            class_name="code-block counter-code-block",
        ),
        background=rx.cond(active, "var(--c-violet-3)", "transparent"),
        border_left=rx.cond(
            active,
            "3px solid var(--c-violet-9)",
            "3px solid transparent",
        ),
        padding="0.875rem 1.5rem",
        class_name="w-full transition-colors",
    )


def counter_code_gap() -> rx.Component:
    return rx.box(height="0.875rem", flex_shrink="0")


def tabs():
    return rx.tabs.root(
        rx.tabs.list(
            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 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",
            class_name="pt-4",
        ),
        rx.tabs.content(
            rx.markdown(
                """Write your backend in the `State` class. Here you can define functions and variables that can be referenced in the frontend. This code runs directly on the server and is not compiled, so there are no special caveats. Here you can use any Python external library and call any method/function.
                """,
            ),
            value="tab2",
            class_name="pt-4",
        ),
        rx.tabs.content(
            rx.markdown(
                """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",
            class_name="pt-4",
        ),
        class_name="text-slate-12 font-normal",
        default_value="tab1",
        value=IntroTabsState.value,
        on_change=lambda x: IntroTabsState.change_value(x),
    )
python
rx.hstack(
    rx.button(
        "Decrement",
        color_scheme="ruby",
        on_click=CounterExampleState.decrement,
    ),
    rx.heading(CounterExampleState.count, font_size="2em"),
    rx.button(
        "Increment",
        color_scheme="grass",
        on_click=CounterExampleState.increment,
    ),
    spacing="4",
)

Here is the full code for this example:

python
tabs()
python
rx.box(
    counter_code_section("""import reflex as rx """, ""),
    counter_code_gap(),
    counter_code_section(
        """class State(rx.State):
    count: int = 0

    @rx.event
    def increment(self):
        self.count += 1

    @rx.event
    def decrement(self):
        self.count -= 1""",
        "tab2",
    ),
    counter_code_gap(),
    counter_code_section(
        """def index():
    return rx.hstack(
        rx.button(
            "Decrement",
            color_scheme="ruby",
            on_click=State.decrement,
        ),
        rx.heading(State.count, font_size="2em"),
        rx.button(
            "Increment",
            color_scheme="grass",
            on_click=State.increment,
        ),
        spacing="4",
    )""",
        "tab1",
    ),
    counter_code_gap(),
    counter_code_section(
        """app = rx.App()
app.add_page(index)""",
        "tab3",
    ),
    class_name=(
        "w-full flex flex-col overflow-hidden rounded-xl border "
        "border-slate-4 bg-slate-2 py-1"
    ),
)

The Structure of a Reflex App

Let's break this example down.

Import

python
import reflex as rx

Import Reflex as rx. All Reflex objects are accessed as rx.*.

State

python
class State(rx.State):
    count: int = 0

State holds the app's mutable data. Variables declared here are called vars. Our counter has one: count, starting at 0.

Event Handlers

python
@rx.event
def increment(self):
    self.count += 1


@rx.event
def decrement(self):
    self.count -= 1

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)

python
def index():
    return rx.hstack(
        rx.button(
            "Decrement",
            color_scheme="ruby",
            on_click=State.decrement,
        ),
        rx.heading(State.count, font_size="2em"),
        rx.button(
            "Increment",
            color_scheme="grass",
            on_click=State.increment,
        ),
        spacing="4",
    )

The UI is built from components (rx.hstack, rx.button, rx.heading) that can be nested and styled with CSS or Tailwind. Reflex ships with 50+ built-in components, and you can wrap any React component.

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.

The sequence goes like this:

  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

python
app = rx.App()
app.add_page(index)

Create the app and register the page at the base route.

Next Steps

🎉 You've built a fully interactive web app in pure Python.

md
# Keep learning

- [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.
md
# Ship faster with AI

- [Reflex Build](https://build.reflex.dev/) — generate a full app from a prompt.
- [Reflex Cloud](/docs/hosting/deploy-quick-start/) — one-command deploy.

Browse our open-source templates, or press Cmd+K / Ctrl+K to search the docs.