R

ReflexKR

Reflex Docs

The Component Workshop: Reflex's 100 Library Components

·

Course overview

Layout

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

Typography

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

Forms

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

Data display

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

Disclosure

·

Tabs

·

Accordion

·

Segmented Control

Overlay

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

Dynamic rendering

·

Cond

·

Match

·

Foreach

·

Auto Scroll

Media

·

Image

·

Audio

·

Video

Tables and data grids

·

Table

·

Data Table

·

Data Editor

Charts

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

Chart parts

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

Other chart libraries

·

Plotly

·

Pyplot

HTML elements

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

Other tools

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

The Component Workshop: Reflex's 100 Library Components
Other tools

Clipboard

rx.clipboard

Official docs

Workshop order

In the bug report form, make an area where a screenshot is attached as soon as the user pastes it.

rx.clipboard listens for paste events and hands the pasted content to the State, sorted by type. It accepts not only text but also binary data such as images. It's an invisible component that draws nothing on screen and listens for pastes that happen in the children it wraps. It has been available since Reflex 0.5.6.

class ClipboardBasicState(rx.State):
    items: list[str] = []

    @rx.event
    def on_paste(self, data: list[tuple[str, str]]):
        self.items = [f"{mime_type}: {item[:60]}" for mime_type, item in data]


rx.vstack(
    rx.clipboard(
        rx.input(placeholder="Copy some text, then paste here"),
        on_paste=ClipboardBasicState.on_paste,
    ),
    rx.foreach(ClipboardBasicState.items, lambda line: rx.code(line, size="1")),
    width="100%",
    max_width="420px",
)

Try it by pasting

rx.clipboard has no visible props, so instead of a prop lab, here is an example that reacts to pasting. Paste text and you get text/plain; paste text copied from a web page and text/html comes along too; paste an image and you get a type such as image/png.

Paste events: 0

class ClipboardTypesState(rx.State):
    count: int = 0
    types: list[str] = []

    @rx.event
    def on_paste(self, data: list[tuple[str, str]]):
        self.count += 1
        self.types = [mime_type for mime_type, _ in data]


rx.card(
    rx.clipboard(
        rx.text_area(placeholder="Paste text, an image, or a copied web page", rows="3"),
        on_paste=ClipboardTypesState.on_paste,
    ),
    rx.text("Paste events: ", ClipboardTypesState.count, size="2", margin_top="8px"),
    rx.flex(rx.foreach(ClipboardTypesState.types, lambda t: rx.badge(t)), spacing="2", wrap="wrap", margin_top="6px"),
    max_width="420px",
)
PropValuesDefaultUse
on_pasteevent handlernoneReceives a list of (MIME type, data) tuples
targetslist of element idsthe children, or the whole document if there are noneElements to listen to for pastes
.stop_propagationevent actionnoneKeeps the event from reaching outer paste handlers
.prevent_defaultevent actionnoneBlocks the default action of actually pasting into the input

The shape of the data

The data that on_paste receives is a list of (mime_type, data) tuples. Text arrives as a plain string, and binary data such as images arrives as a base64-encoded data URI (data:image/png;base64,...). That string can go straight into the src of rx.image, and if you need the bytes on the server, you can decode it in Python with urllib.request.urlopen(data_uri).read().

Receiving pastes across the whole page

Place just rx.clipboard() with no children and it attaches to the document's paste event, so it fires wherever on the page you paste. Doing that on this page would catch the pastes of every other example too, so the official docs' example is shown as code only.

class ClipboardPasteState(rx.State):
    @rx.event
    def on_paste(self, data: list[tuple[str, str]]):
        for mime_type, item in data:
            yield rx.toast(f"Pasted {mime_type} data: {item}")


def clipboard_example():
    return rx.fragment(
        rx.clipboard(on_paste=ClipboardPasteState.on_paste),   # no children: listens for paste on the whole document
        "Paste Content Here",
    )

Scoped pasting

To receive pastes only in a particular element, wrap that element in rx.clipboard. If there's a page-wide handler outside, use .stop_propagation to keep the event from bubbling up. Add .prevent_default and the text isn't actually pasted into the input. Paste some text into the second input and the field stays empty.

No image yet. Copy an image (or take a screenshot) and paste above.

class ClipboardImageState(rx.State):
    last_image_uri: str = ""

    @rx.event
    def on_paste(self, data: list[tuple[str, str]]):
        for mime_type, item in data:
            if mime_type.startswith("image/"):
                if len(item) > 2_000_000:
                    return rx.toast("This image is too large for the demo")
                self.last_image_uri = item
                break
        else:
            return rx.toast("Did not find an image in the pasted data")


rx.vstack(
    rx.clipboard(
        rx.input(placeholder="Paste Image (stop propagation)"),
        on_paste=ClipboardImageState.on_paste.stop_propagation,
    ),
    rx.clipboard(
        rx.input(placeholder="Paste Image (prevent default)"),
        on_paste=ClipboardImageState.on_paste.prevent_default,
    ),
    rx.cond(
        ClipboardImageState.last_image_uri != "",
        rx.image(src=ClipboardImageState.last_image_uri, alt="Image pasted from clipboard", max_width="240px", border_radius="6px"),
        rx.text("No image yet. Copy an image (or take a screenshot) and paste above.", size="2", color_scheme="gray"),
    ),
    width="100%",
    max_width="420px",
)

targets: pointing by id instead of wrapping

Give targets the ids of elements and it listens to just those elements without wrapping them. When you wrap children, Reflex adds an id to them and fills in targets automatically.

Pasted characters: 0

class ClipboardTargetState(rx.State):
    pasted_chars: int = 0

    @rx.event
    def on_paste(self, data: list[tuple[str, str]]):
        self.pasted_chars += sum(len(item) for mime_type, item in data if mime_type == "text/plain")


rx.vstack(
    rx.clipboard(on_paste=ClipboardTargetState.on_paste, targets=["clipboard_target_note"]),
    rx.text_area(id="clipboard_target_note", placeholder="Only this box is watched", rows="2"),
    rx.input(placeholder="This box is not watched"),
    rx.text("Pasted characters: ", ClipboardTargetState.pasted_chars, size="2"),
    width="100%",
    max_width="420px",
)

Filling the order

Click the dashed box to focus it, paste a screenshot, and the image is attached. The box isn't an input, so tab_index=0 lets it receive focus, and event actions are chained, as in .prevent_default.stop_propagation. The image goes into the State as a data URI, so images that are too large are filtered out.

Bug report

Click here, then paste a screenshot (Ctrl + V)

class ClipboardTicketState(rx.State):
    shot: str = ""
    note: str = ""

    @rx.event
    def on_paste(self, data: list[tuple[str, str]]):
        for mime_type, item in data:
            if mime_type.startswith("image/") and len(item) <= 2_000_000:
                self.shot = item
                return
        return rx.toast("Paste a screenshot (an image) here")

    @rx.event
    def change_note(self, value: str):
        self.note = value

    @rx.event
    def clear_shot(self):
        self.shot = ""


rx.card(
    rx.heading("Bug report", size="3"),
    rx.text_area(
        value=ClipboardTicketState.note,
        on_change=ClipboardTicketState.change_note,
        placeholder="What went wrong?",
        rows="2",
        margin_y="8px",
    ),
    rx.clipboard(
        rx.box(
            rx.cond(
                ClipboardTicketState.shot != "",
                rx.image(src=ClipboardTicketState.shot, alt="Pasted screenshot", max_height="160px"),
                rx.text("Click here, then paste a screenshot (Ctrl + V)", size="2", color_scheme="gray"),
            ),
            tab_index=0,
            padding="16px",
            border=f"2px dashed {rx.color('gray', 7)}",
            border_radius="8px",
            text_align="center",
        ),
        on_paste=ClipboardTicketState.on_paste.prevent_default.stop_propagation,
    ),
    rx.button("Remove screenshot", on_click=ClipboardTicketState.clear_shot, variant="soft", color_scheme="gray", size="1", margin_top="8px"),
    max_width="420px",
)

Workshop notes

  • rx.clipboard(child, on_paste=...) listens for pastes in its children. Without children, the whole document.

  • on_paste receives [(mime_type, data), ...]. Binary data is a base64 data URI.

  • A data URI can be used directly as the src of rx.image.

  • .stop_propagation keeps it from outer handlers; .prevent_default blocks the actual paste.

  • targets=[id, ...] listens to specific elements without wrapping them.

← Previous

Other tools · Skeleton

Next →

Other tools · Html Embed

Get news about new lessons

We'll email you when this course gets new lessons or a follow-up course opens.

We collect: your email address · Purpose: news about new lessons or the launch of this course · Kept until: you ask us to remove it. We never use it for anything else or share it. Reply to any notice email and we'll delete it right away.

Questions & answers

Ask anything about this lesson. Questions from every language version of this page appear together.

Shown as plain text, without links or formatting.

R

ReflexKR

Learn Python and Reflex by doing. Every example was run and verified on Reflex 0.10.0.

ReflexKR is free, with no banner ads. If it helped you, you can support it with a coffee. ☕ Buy us a coffee

Affiliate disclosure
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex