├── .gitignore
├── static
└── fonts
│ └── JetBrainsMono-Regular.woff2
├── README.md
├── config.toml
├── LICENSE.md
├── templates
└── index.html
├── sass
└── style.scss
└── content
└── _index.md
/.gitignore:
--------------------------------------------------------------------------------
1 | public
--------------------------------------------------------------------------------
/static/fonts/JetBrainsMono-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marc2332/dioxus-cheatsheet/HEAD/static/fonts/JetBrainsMono-Regular.woff2
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🦀 Dioxus Cheatsheet
2 |
3 | Website: https://dioxus-cheatsheet.vercel.app/
4 |
5 | ⚠️ **Work in progress** cheatsheet for [🧬 Dioxus](https://github.com/DioxusLabs/dioxus) made with [Zola](https://www.getzola.org/).
6 |
7 | Feel free to make any suggestion, corrections or whatever, **contributions** are welcome 🚪👋!!
8 |
9 | ## Development
10 |
11 | ```bash
12 | zola serve
13 | ```
14 |
15 | MIT License
--------------------------------------------------------------------------------
/config.toml:
--------------------------------------------------------------------------------
1 | # The URL the site will be built for
2 | base_url = "https://dioxus-cheatsheet.vercel.app/"
3 |
4 | # Whether to automatically compile all Sass files in the sass directory
5 | compile_sass = true
6 |
7 | # Whether to build a search index to be used later on by a JavaScript library
8 | build_search_index = true
9 |
10 | [markdown]
11 | # Whether to do syntax highlighting
12 | # Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
13 | highlight_code = true
14 | highlight_theme = "gruvbox-dark"
15 |
16 | [extra]
17 | # Put all your custom variables here
18 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Marc Espín Sanz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ section.title }}
9 |
10 |
11 |
12 |
13 | {% block title %}{{ config.title }}{% endblock title %}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
{{ section.title }}
24 |
⚠️ WORK IN PROGRESS ! ⚠️
25 |
Source Code 🐙
26 | {{ section.content | safe }}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/sass/style.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "JetBrains Mono";
3 | src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2");
4 | font-weight: 500;
5 | font-style: normal;
6 | font-display: swap;
7 | }
8 |
9 |
10 | :root {
11 | background: #222;
12 | color: #ccc;
13 | }
14 |
15 | .title {
16 | text-align: center;
17 | margin-top: 60px;
18 | margin-bottom: 30px;
19 | font-size: 40px;
20 | }
21 |
22 | .subtitle {
23 | text-align: center;
24 | margin: 20px 0px;
25 | }
26 |
27 | .small-subtitle {
28 | text-align: center;
29 | margin-top: 10px;
30 | margin-bottom: 40px;
31 | }
32 |
33 | a {
34 | color: #4183c4;
35 | }
36 |
37 | .page {
38 | margin: 0 auto;
39 | width: 800px;
40 |
41 | @media screen and (max-width: 800px) {
42 | & {
43 | padding: 10px;
44 | width: 90%;
45 | }
46 | }
47 | }
48 |
49 | * {
50 | font-family: system-ui;
51 | }
52 |
53 | toc {
54 | display: flex;
55 | margin-right: auto;
56 | margin-left: 200px;
57 | width: 70%;
58 | column {
59 | width: 50%;
60 | }
61 | @media screen and (max-width: 800px) {
62 | & {
63 | display: block;
64 | margin-right: auto;
65 | margin-left: auto;
66 | width: 80%;
67 | }
68 | }
69 | }
70 |
71 | pre {
72 | padding: 15px;
73 | border-radius: 10px;
74 | overflow: auto;
75 | }
76 |
77 | code > * {
78 | font-family: 'JetBrains Mono';
79 | }
80 |
81 | .zola-anchor {
82 | margin-left: -30px;
83 | font-size: 1.25rem;
84 | opacity: 0;
85 | }
86 |
87 | h1:hover, h2:hover, h3:hover, h4:hover {
88 | .zola-anchor {
89 | opacity: 1;
90 | }
91 | }
--------------------------------------------------------------------------------
/content/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | title = "🦀 Dioxus Library Cheatsheet"
3 | insert_anchor_links = "left"
4 | in_search_index = true
5 | +++
6 |
7 |
8 |
9 |
10 | **Components**
11 |
12 | - [Components](#components)
13 | - [Props](#props)
14 | - [RSX](#rsx)
15 | - ⚠️ Lifecycle
16 |
17 | **State**
18 |
19 | - [Local](#local-state)
20 | - [Shared](#shared-state)
21 | - [Global](#global-state)
22 |
23 | **Hooks**
24 |
25 | - ⚠️ Rules
26 | - ⚠️ Custom
27 |
28 |
29 |
30 |
31 |
32 | **Patterns**
33 |
34 | - [Composition](#composition)
35 | - [Memoization](#memoization)
36 |
37 | **Dioxus Standard Library**
38 |
39 | - ⚠️ Color Scheme
40 | - ⚠️ Localization (i18n)
41 | - ⚠️ Channels
42 |
43 | **Renderers**
44 |
45 | - ⚠️ Web
46 | - ⚠️ Desktop
47 | - ⚠️ TUI
48 | - ⚠️ Blitz
49 | - ⚠️ Liveview
50 | - ⚠️ SSR
51 | - ⚠️ Fullstack
52 | - ⚠️ Freya
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | ### Overview
61 |
62 | **Dioxus** is a declarative library for building user interfaces (UI) in Rust 🦀. It is inspired by React, so it shares many of it's concepts like **Components** and **Hooks**. Dioxus is renderer agnostic, so you can use it with any renderer that is available, for example, there are renderers for web (WASM), Desktop (Webview), TUI, Blitz (WGPU), LiveView, Freya (non-official Skia renderer), etc.
63 |
64 | ### Components
65 |
66 | **Components** are the building blocks of your application. They encapsulate contain the declared UI of your app.
67 | In Dioxus they are expressed in the form of functions.
68 |
69 | ```rust
70 | fn Component(cx: Scope) -> Element {
71 | render!(
72 | div {
73 | "Hello World"
74 | }
75 | )
76 | }
77 | ```
78 |
79 | The function takes a `Scope` as an argument and returns an `Element`.
80 |
81 | - **Scopes** are unique to their components. This is why you will need to pass it to **Hooks** when calling them.
82 | - **Element** is the representation of your UI that will be generated by `render!()` or `cx.render(rsx!())`.
83 |
84 | ## RSX
85 |
86 | RSX is a special syntax used to declare the UI that integrates with Rust.
87 |
88 | ```rust
89 | fn app(cx: Scope) -> Element {
90 | render!(
91 | div {} // A div element
92 | Component {} // The `CoolComponent` component
93 | )
94 | }
95 |
96 | fn CoolComponent(cx: Scope) -> Element {
97 | let some_value = 123;
98 |
99 | render!(
100 | button { // A button element
101 | width: "100%", // An attribute
102 | onclick: |evt| println!("{evt:?}"), // An event listener
103 | p { // A nested element
104 | height: "20px",
105 | "Value is {some_value}" // A text node
106 | }
107 | }
108 | )
109 | }
110 | ```
111 |
112 | ### Local State
113 |
114 | Components can have local state, this is useful for storing data that is only relevant to the component itself. This can be archived by using the `use_state` or `use_ref` hooks.
115 |
116 | #### `use_state`
117 |
118 | The `use_state` hook is used to store data that will change over time. It takes an initial value and returns a `UseState` object.
119 |
120 | Every time you modify the state, the component will be re-run.
121 |
122 | ```rust
123 | fn Component(cx: Scope) -> Element {
124 | let counter = use_state(cx, || 0);
125 |
126 | // This will be printed every time the state changes
127 | println!("{counter}");
128 |
129 | let onclick = |_| {
130 | // You can use the normal operators to update the state
131 | counter += 1;
132 |
133 | // Or you can use the set and get functions
134 | counter.set(counter.get() + 1);
135 |
136 | // You can also use the modify function
137 | counter.modify(|counter| counter + 1);
138 |
139 | // Or the with_mut function
140 | counter.modify(|counter| *counter += 1);
141 | };
142 |
143 | render!(
144 | button {
145 | onclick: onclick,
146 | "{counter}"
147 | }
148 | )
149 | }
150 | ```
151 |
152 | #### `use_ref`
153 |
154 | The use_ref hook is used to store data that will change over time. It takes an initial value and returns a `UseRef` object.
155 | The difference with `use_state` is that `use_ref` can be modified without re-running the component, which can be useful when you need to hold a value across renders that is used asynchronously.
156 |
157 | ```rust
158 | fn Component(cx: Scope) -> Element {
159 | let counter = use_ref(cx, || 0);
160 |
161 | let onclick = |_| {
162 | // Silently increment the counter, this will not make the component re-run
163 | counter.write_silent() = counter.read() + 1;
164 | };
165 |
166 | let double_and_update = |_| {
167 | // Double the counter, this will make the component re-run
168 | counter.write() = counter.read() * 2;
169 | };
170 |
171 | render!(
172 | button {
173 | onclick: increment_silently,
174 | "Increment Silently"
175 | }
176 | p {
177 | "{counter}"
178 | }
179 | button {
180 | onclick: double_and_update,
181 | "Double and update"
182 | }
183 | )
184 | }
185 | ```
186 |
187 | ### Shared State
188 |
189 | Shared state is used to store data that is shared across components. This can be archived by using the `use_shared_state` and `use_shared_state_provider` hooks.
190 |
191 | #### `use_shared_state_provider`
192 |
193 | You can use `use_shared_state_provider` to share a certain value all the way down to it's children.
194 |
195 | ```rust
196 |
197 | #[derive(Debug, Clone)]
198 | enum Theme {
199 | Light,
200 | Dark
201 | }
202 |
203 | fn App(cx: Scope) -> Element {
204 | // All the children of this component will have access to this shared state
205 | // See `use_shared_state` on how to actually use it
206 | use_shared_state_provider(cx, || Theme::Dark);
207 |
208 | render!(
209 | CoolChild {}
210 | )
211 | }
212 | ```
213 |
214 | #### `use_shared_state`
215 |
216 | You can use `use_shared_state` to access a value shared by one of the component ancestors.
217 |
218 | ```rust
219 |
220 | #[derive(Debug, Clone)]
221 | enum Theme {
222 | Light,
223 | Dark
224 | }
225 |
226 | fn CoolChild(cx: Scope) -> Element {
227 | // Access the shared state
228 | let theme = use_shared_state::(cx);
229 |
230 | // You can read it with the read function
231 | let is_light = theme.read() == Theme::Light;
232 |
233 | let onclick = |_| {
234 | // Or modify it with the write function
235 | theme.write() = match theme.read() {
236 | Theme::Light => Theme::Dark,
237 | Theme::Dark => Theme::Light
238 | };
239 | };
240 |
241 | render!(
242 | button {
243 | onclick: onclick,
244 | "Click to toggle the theme: {theme:?}"
245 | }
246 | )
247 | }
248 | ```
249 |
250 | ### Global State
251 |
252 | ### Providers
253 |
254 | Providers are usually Hooks/Components that provide a certain value down to all it's children
255 |
256 | Example:
257 |
258 | ```rust
259 |
260 | fn App(cx: Scope) -> Element {
261 | render!(
262 | // Only Components that are desdendants of ThemeProvider will have access to the state
263 | ThemeProvider {
264 | CoolContainer { // ✅ Will have access
265 | CoolButton { // ✅ Will have access
266 | "Hello World"
267 | }
268 | }
269 | }
270 | NotCoolText { // ❌ Will not have access
271 | "This text is not cool"
272 | }
273 | )
274 | }
275 | ```
276 |
277 | ### Props
278 |
279 | Props are parameters for Components, but they are different to normal function arguments:
280 |
281 | - Can be optional
282 | - Can have default types
283 | - Can be memoized
284 |
285 | Props as struct:
286 |
287 | ```rust
288 | #[derive(Props)]
289 | struct CoolProps {
290 | value: i32
291 | }
292 |
293 | fn CoolValue(cx: Scope) -> Element {
294 | let value = cx.props.value;
295 | render!(
296 | p {
297 | "Value: {value}"
298 | }
299 | )
300 | }
301 |
302 | fn Component(cx: Scope) -> Element {
303 | let value = use_state(cx, || 0);
304 | render!(
305 | button {
306 | onclick: |evt| {
307 | value += 1;
308 | },
309 | CoolValue {
310 | value: *value.get()
311 | }
312 | }
313 | )
314 | }
315 | ```
316 |
317 | Inline props:
318 |
319 | ```rust
320 | #[inline_props]
321 | fn CoolValue(cx: Scope, value: i32) -> Element {
322 | render!(
323 | p {
324 | "Value: {value}"
325 | }
326 | )
327 | }
328 |
329 | fn Component(cx: Scope) -> Element {
330 | let value = use_state(cx, || 0);
331 | render!(
332 | button {
333 | onclick: |evt| {
334 | value += 1;
335 | },
336 | CoolValue {
337 | value: *value.get()
338 | }
339 | }
340 | )
341 | }
342 | ```
343 |
344 | Props with event handlers:
345 |
346 | ```rust
347 | struct CoolEvent {
348 | value: i32
349 | }
350 |
351 | #[derive(Props)]
352 | struct CoolProps<'a> {
353 | value: i32,
354 | oncoolevent: EventHandler<'a, CoolEvent>
355 | }
356 |
357 | fn CoolValue(cx: Scope) -> Element {
358 | let value = cx.props.value;
359 | render!(
360 | button {
361 | onclick: |_| cx.props.oncoolevent.call(CoolEvent { value: value + 1}),
362 | "Value: {value}"
363 | }
364 | )
365 | }
366 |
367 | fn Component(cx: Scope) -> Element {
368 | let value = use_state(cx, || 0);
369 | render!(
370 | CoolValue {
371 | oncoolevent: |evt| {
372 | value.set(evt.value)
373 | },
374 | value: *value.get()
375 | }
376 | )
377 | }
378 | ```
379 |
380 | Optional props:
381 |
382 | ```rust
383 |
384 | #[derive(Props)]
385 | struct CoolProps {
386 | #[props(optional)]
387 | value: Option,
388 | }
389 |
390 | fn CoolValue(cx: Scope) -> Element {
391 | let value = cx.props.value.unwrap_or(123);
392 | render!(
393 | p {
394 | "Value: {value}"
395 | }
396 | )
397 | }
398 |
399 | fn Component(cx: Scope) -> Element {
400 | render!(
401 | CoolValue { }
402 | )
403 | }
404 | ```
405 |
406 | Optional props with default values:
407 |
408 | ```rust
409 | #[derive(Props)]
410 | struct CoolProps<'a> {
411 | #[props(optional, default = 123)]
412 | value: i32,
413 | }
414 |
415 | fn CoolValue(cx: Scope) -> Element {
416 | let value = cx.props.value;
417 | render!(
418 | p {
419 | "Value: {value}"
420 | }
421 | )
422 | }
423 |
424 | fn Component(cx: Scope) -> Element {
425 | render!(
426 | CoolValue { }
427 | )
428 | }
429 | ```
430 |
431 | Auto convert prop values:
432 |
433 | ```rust
434 | #[derive(Props)]
435 | struct CoolProps<'a> {
436 | #[props(into)]
437 | text: String,
438 | }
439 |
440 | fn CoolValue(cx: Scope) -> Element {
441 | let text = cx.props.text;
442 | render!(
443 | p {
444 | "text: {text}"
445 | }
446 | )
447 | }
448 |
449 | fn Component(cx: Scope) -> Element {
450 | render!(
451 | CoolValue {
452 | text: "Hello World"
453 | }
454 | )
455 | }
456 | ```
457 |
458 | ### Hooks
459 |
460 | Hooks are functions that allow you to do certain things in your components. They are usually prefixed with `use_`. Because they are simple functions you can create your own hooks:
461 |
462 | ```rust
463 | #[derive(Clone)]
464 | struct UseCounter {
465 | state: UseState
466 | }
467 |
468 | impl UseCounter {
469 | fn increment(&self) {
470 | self.state.set(self.state.get() + 1);
471 | }
472 |
473 | fn value(&self) -> i32 {
474 | *self.state.get()
475 | }
476 | }
477 |
478 | fn use_counter(cx: ScopeState) -> UseCounter {
479 | let state = use_state(cx, || 0);
480 | UseCounter { state }
481 | }
482 |
483 | fn Component(cx: Scope) -> Element {
484 | let counter = use_counter();
485 |
486 | render!(
487 | button {
488 | onclick: |_| counter.increment(),
489 | "{counter.value()}"
490 | }
491 | )
492 | }
493 | ```
494 |
495 | ### Composition
496 |
497 | Composition is the ability to combine multiple components to form a connected UI flow. Because you use components directly it becomes easier to create more complex scenarios.
498 |
499 | Without composition 🥲:
500 |
501 | ```rust
502 | struct CoolOption(String, bool);
503 |
504 | fn app() -> Element {
505 | render!(
506 | CoolDropdown {
507 | options: vec![
508 | CoolOption("Option 1".to_string(), false),
509 | CoolOption("Option 2".to_string(), true),
510 | CoolOption("Option 3".to_string())
511 | ]
512 | }
513 | )
514 | }
515 | ```
516 |
517 | With composition 😎:
518 |
519 | ```rust
520 | fn app() -> Element {
521 | render!(
522 | CoolDropdown {
523 | CoolDropdownOption {
524 | big: true,
525 | "Option 1"
526 | }
527 | CoolDropdownOption {
528 | "Option 2"
529 | }
530 | CoolDropdownOption {
531 | "Option 3"
532 | }
533 | }
534 | )
535 | }
536 | ```
537 |
538 | ### Memoization
539 |
540 | Memoization (or caching) is used to avoid re-running components unnecessarily.
541 |
542 | Without memoization 🥲:
543 |
544 | ```rust
545 |
546 | #[inline_props]
547 | fn CoolComponent<'a>(cx: Scope<'a>, name: &'a str) -> Element<'a> {
548 | render!(
549 | rect {
550 | "Hello, {name}!"
551 | }
552 | )
553 | }
554 |
555 | fn app(cx: Scope) -> Element {
556 |
557 | // `CoolComponent` will re-run whenever the component `app` re-runs,
558 | // which is always as it's not an owned value.
559 |
560 | render!(
561 | CoolComponent {
562 | name: "World"
563 | }
564 | )
565 | }
566 | ```
567 |
568 | With memoization 😎:
569 |
570 | ```rust
571 |
572 | #[inline_props]
573 | fn CoolComponent(cx: Scope, name: String) -> Element {
574 | render!(
575 | rect {
576 | "Hello, {name}!"
577 | }
578 | )
579 | }
580 |
581 | fn app(cx: Scope) -> Element {
582 |
583 | // Even when this component `app` re-runs multiple times,
584 | // the `CoolComponent` will only run once and re-run when the `name` prop changes,
585 | // which is essentially never as it's harcoded to "World" and is an owned value.
586 |
587 | render!(
588 | CoolComponent {
589 | name: "World".to_string()
590 | }
591 | )
592 | }
593 | ```
594 |
--------------------------------------------------------------------------------