|
| 1 | +# Render and compose |
| 2 | + |
| 3 | +A common question that comes up on the [Textual Discord server](https://discord.gg/Enf6Z3qhVr) is what is the difference between [`render`][textual.widget.Widget.render] and [`compose`][textual.widget.Widget.compose] methods on a widget? |
| 4 | +In this article we will clarify the differences, and use both these methods to build something fun. |
| 5 | + |
| 6 | +<div class="video-wrapper"> |
| 7 | +<iframe width="1280" height="922" src="https://www.youtube.com/embed/dYU7jHyabX8" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> |
| 8 | +</div> |
| 9 | + |
| 10 | +## Which method to use? |
| 11 | + |
| 12 | +Render and compose are easy to confuse because they both ultimately define what a widget will look like, but they have quite different uses. |
| 13 | + |
| 14 | +The `render` method on a widget returns a [Rich](https://rich.readthedocs.io/en/latest/) renderable, which is anything you could print with Rich. |
| 15 | +The simplest renderable is just text; so `render()` methods often return a string to display inside the widget, but could equally return a [`Text`](https://rich.readthedocs.io/en/latest/text.html) instance, a [`Table`](https://rich.readthedocs.io/en/latest/tables.html), or anything else from Rich (or third party library). |
| 16 | + |
| 17 | +The `compose` method is used to build [*compound* widgets](../guide/widgets.md#compound-widgets) (widgets composed of other widgets). |
| 18 | + |
| 19 | +A general rule of thumb, is that if you implement a `compose` method, there is no need for a `render` method because it is the widgets yielded from `compose` which define how the custom widget will look. |
| 20 | +However, you *can* mix these two methods. |
| 21 | +If you implement both, the `render` method will set the custom widget's *background* and `compose` will add widgets on top of that background. |
| 22 | + |
| 23 | +## Combining render and compose |
| 24 | + |
| 25 | +Let's look at an example that combines both these methods. |
| 26 | +We will create a custom widget with a [linear gradient][textual.renderables.gradient.LinearGradient] as a background. |
| 27 | +The background will be animated (I did promise *fun*)! |
| 28 | + |
| 29 | +=== "render_compose.py" |
| 30 | + |
| 31 | + ```python |
| 32 | + --8<-- "docs/examples/how-to/render_compose.py" |
| 33 | + ``` |
| 34 | + |
| 35 | + 1. Refresh the widget 30 times a second. |
| 36 | + 2. Compose our compound widget, which contains a single Static. |
| 37 | + 3. Render a linear gradient in the background. |
| 38 | + |
| 39 | +=== "Output" |
| 40 | + |
| 41 | + ```{.textual path="docs/examples/how-to/render_compose.py" columns="100" lines="40"} |
| 42 | + ``` |
| 43 | + |
| 44 | +The `Splash` custom widget has a `compose` method which adds a simple `Static` widget to display a message. |
| 45 | +Additionally there is a `render` method which returns a renderable to fill the background with a gradient. |
| 46 | + |
| 47 | +!!! tip |
| 48 | + |
| 49 | + As fun as this is, spinning animated gradients may be too distracting for most apps! |
| 50 | + |
| 51 | +## Summary |
| 52 | + |
| 53 | +Keep the following in mind when building [custom widgets](../guide/widgets.md). |
| 54 | + |
| 55 | +1. Use `render` to return simple text, or a Rich renderable. |
| 56 | +2. Use `compose` to create a widget out of other widgets. |
| 57 | +3. If you define both, then `render` will be used as a *background*. |
| 58 | + |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +We are here to [help](../help.md)! |
0 commit comments