2626
2727@no_example ()
2828def ui (fn : Callable [P , R ]) -> Callable [Concatenate [str , P ], R ]:
29+ """Decorator for defining a Shiny module UI function.
30+
31+ This decorator allows you to write the UI portion of a Shiny module.
32+ This enables reuse of UI components and consistent input/output handling
33+ when paired with a `@module.server` function.
34+
35+ Parameters
36+ ----------
37+ fn : Callable[..., R]
38+ A function that returns a Shiny UI element or layout (e.g., a `ui.panel_*` component).
39+ This function should **not** accept an `id` parameter itself; the decorator injects it.
40+
41+ Returns
42+ -------
43+ Callable[[str, ...], R]
44+ A function that takes a `str` `id` as its first argument, followed by any additional
45+ parameters accepted by `fn`. When called, it returns UI elements with input/output
46+ IDs automatically namespaced using the provided module `id`.
47+
48+
49+ Example
50+ -------
51+
52+ ```python
53+ from shiny import App, module, reactive, render, ui
54+
55+
56+ @module.ui
57+ def counter_ui(label: str = "Increment counter") -> ui.TagChild:
58+ return ui.card(
59+ ui.h2("This is " + label),
60+ ui.input_action_button(id="button", label=label),
61+ ui.output_code(id="out"),
62+ )
63+
64+
65+ @module.server
66+ def counter_server(input, output, session, starting_value: int = 0):
67+ count: reactive.value[int] = reactive.value(starting_value)
68+
69+ @reactive.effect
70+ @reactive.event(input.button)
71+ def _():
72+ count.set(count() + 1)
73+
74+ @render.code
75+ def out() -> str:
76+ return f"Click count is {count()}"
77+
78+
79+ app_ui = ui.page_fluid(
80+ counter_ui("counter1", "Counter 1"),
81+ counter_ui("counter2", "Counter 2"),
82+ )
83+
84+
85+ def server(input, output, session):
86+ counter_server("counter1")
87+ counter_server("counter2")
88+
89+
90+ app = App(app_ui, server)
91+ ```
92+
93+ See Also
94+ --------
95+ Shiny Modules documentation:
96+ https://shiny.posit.co/py/docs/modules.html
97+ """
98+
2999 def wrapper (id : Id , * args : P .args , ** kwargs : P .kwargs ) -> R :
30100 with namespace_context (id ):
31101 return fn (* args , ** kwargs )
@@ -37,12 +107,89 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
37107def server (
38108 fn : Callable [Concatenate [Inputs , Outputs , Session , P ], R ],
39109) -> Callable [Concatenate [str , P ], R ]:
110+ """Decorator for defining a Shiny module server function.
111+
112+ This decorator is used to encapsulate the server logic for a Shiny module.
113+ It automatically creates a namespaced child `Session` using the provided module `id`,
114+ and passes the appropriate `input`, `output`, and `session` objects to your server function.
115+
116+ This ensures that the server logic is scoped correctly for each module instance and
117+ allows for reuse of logic across multiple instances of the same module.
118+
119+ Parameters
120+ ----------
121+ fn : Callable[[Inputs, Outputs, Session, ...], R]
122+ A server function that takes `input`, `output`, and `session` as its first
123+ three arguments, followed by any additional arguments defined by the user.
124+
125+ Returns
126+ -------
127+ Callable[[str, ...], R]
128+ A function that takes a module `id` (as a string) as its first argument,
129+ followed by any arguments expected by `fn`. When called, it will register
130+ the module's server logic in a namespaced context.
131+
132+ Example
133+ -------
134+
135+ ```python
136+ from shiny import App, module, reactive, render, ui
137+
138+
139+ @module.ui
140+ def counter_ui(label: str = "Increment counter") -> ui.TagChild:
141+ return ui.card(
142+ ui.h2("This is " + label),
143+ ui.input_action_button(id="button", label=label),
144+ ui.output_code(id="out"),
145+ )
146+
147+
148+ @module.server
149+ def counter_server(input, output, session, starting_value: int = 0):
150+ count: reactive.value[int] = reactive.value(starting_value)
151+
152+ @reactive.effect
153+ @reactive.event(input.button)
154+ def _():
155+ count.set(count() + 1)
156+
157+ @render.code
158+ def out() -> str:
159+ return f"Click count is {count()}"
160+
161+
162+ app_ui = ui.page_fluid(
163+ counter_ui("counter1", "Counter 1"),
164+ counter_ui("counter2", "Counter 2"),
165+ )
166+
167+
168+ def server(input, output, session):
169+ counter_server("counter1")
170+ counter_server("counter2")
171+
172+
173+ app = App(app_ui, server)
174+ ```
175+
176+ See Also
177+ --------
178+ Shiny Modules documentation:
179+ https://shiny.posit.co/py/docs/modules.html
180+ """
40181 from .session import require_active_session , session_context
41182
42183 def wrapper (id : Id , * args : P .args , ** kwargs : P .kwargs ) -> R :
43184 sess = require_active_session (None )
44185 child_sess = sess .make_scope (id )
45186 with session_context (child_sess ):
46- return fn (child_sess .input , child_sess .output , child_sess , * args , ** kwargs )
187+ return fn (
188+ child_sess .input ,
189+ child_sess .output ,
190+ child_sess ,
191+ * args ,
192+ ** kwargs ,
193+ )
47194
48195 return wrapper
0 commit comments