diff --git a/README.md b/README.md
index 632c19da5..cdad238d0 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
```rust
-static App: FC<()> = |(cx, props)| {
+fn App((cx, props): Component<()>) -> Element {
let mut count = use_state(cx, || 0);
cx.render(rsx!(
diff --git a/docs/blog/01-release.md b/docs/blog/01-release.md
deleted file mode 100644
index 545e3ee12..000000000
--- a/docs/blog/01-release.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-- Rust's smart pointers make use_state extremely easy and fun to work with.
-- Multiple flavors (raw, html, rsx) of templating to pick-and-choose for the type of component.
-- Extensible DSL through traits and impls plus support for WebComponents.
-- Simple-to-implement trait for new reconcilers.
-- Fast retained-mode server-side-renderer that works with buffered writers, files, and strings.
-- Compile-time correct inline CSS as well as global styling.
-- Integrated "signal" system allows for near-instant updates by skipping the diff algorithm entirely.
-- Built-in cooperative scheduler (IE React's fiber mechanism).
-- Built-in asynchronous scheduler for coroutines and tasks within components.
-- Built-in suspense scheduler for Rust's futures.
-- Extremely fast diffing algorithm for the most complex of apps.
-- Runs natively on mobile and on desktop with no need for a 3rd party JS engine.
-- Drastically fewer runtime errors and crashes with first-class error handling.
-- Extremely powerful iterator and optional chaining integration for fast and robust apps.
diff --git a/docs/src/README.md b/docs/src/README.md
index 339f2a76f..33cd7c7f1 100644
--- a/docs/src/README.md
+++ b/docs/src/README.md
@@ -4,9 +4,8 @@
**Dioxus** is a framework and ecosystem for building fast, scalable, and robust user interfaces with the Rust programming language. This guide will help you get up-and-running with Dioxus running on the Web, Desktop, Mobile, and more.
-```rust, ignore
-// An example Dioxus app - closely resembles React
-static App: FC<()> = |(cx, props)| {
+```rust
+fn App((cx, props): Component<()>) -> Element {
let mut count = use_state(cx, || 0);
cx.render(rsx!(
@@ -20,7 +19,6 @@ static App: FC<()> = |(cx, props)| {
The Dioxus API and patterns closely resemble React - if this guide is lacking in any general concept or an error message is confusing, we recommend substituting "React" for "Dioxus" in your web search terms. A major goal of Dioxus is to provide a familiar toolkit for UI in Rust, so we've chosen to follow in the footsteps of popular UI frameworks (React, Redux, etc) - if you know React, then you already know Dioxus. If you don't know either, this guide will still help you!
-
### Web Support
---
@@ -79,23 +77,27 @@ Examples:
---
Mobile is currently the least-supported renderer target for Dioxus. Mobile apps are rendered with the platform's WebView, meaning that animations, transparency, and native widgets are not currently achievable. In addition, iOS is the only supported Mobile Platform. It is possible to get Dioxus running on Android and rendered with WebView, but the Rust windowing library that Dioxus uses - tao - does not currently supported Android.
+Mobile support is currently best suited for CRUD-style apps, ideally for internal teams who need to develop quickly but don't care much about animations or native widgets.
+
[Jump to the getting started guide for Mobile.]()
Examples:
- [Todo App]()
- [Chat App]()
-### LiveView Support
+### LiveView / Server Component Support
---
-The internal architecture of Dioxus was designed from day one to support the `LiveView` use-case, where a web server hosts a running app for each connected user. As of today, there is no out-of-the-box LiveView support - you'll need to wire this up yourself. While not currently fully implemented, the expectation is that LiveView apps can be a hybrid between Wasm and server-rendered where only portions of a page are "live" and the rest of the page is either server-rendered, statically generated, or handled by the host SPA.
-
+The internal architecture of Dioxus was designed from day one to support the `LiveView` use-case, where a web server hosts a running app for each connected user. As of today, there is no first-class LiveView support - you'll need to wire this up yourself.
+While not currently fully implemented, the expectation is that LiveView apps can be a hybrid between Wasm and server-rendered where only portions of a page are "live" and the rest of the page is either server-rendered, statically generated, or handled by the host SPA.
### Multithreaded Support
---
-The Dioxus VirtualDom, sadly, is not `Send.` This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc. Currently, your two options include:
-- Actix: Actix supports `!Send` handlers
-- VirtualDomPool: A thread-per-core VirtualDom pool that uses message passing and serialization
+The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe. This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc.
-When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String - but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a VirtualDomPool which solves the `Send` problem.
+To solve this, you'll want to spawn a VirtualDom on its own thread and communicate with it via channels.
+
+When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String - but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a pool of VirtualDoms.
+
+Ultimately, you can always wrap the VirtualDom with a `Send` type and manually uphold the `Send` guarantees yourself.
diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md
index 2718e5b2d..9255f443d 100644
--- a/docs/src/SUMMARY.md
+++ b/docs/src/SUMMARY.md
@@ -3,22 +3,47 @@
- [Introduction](README.md)
- [Getting Setup](setup.md)
- [Hello, World!](hello_world.md)
-- [Core Topics](concepts/00-index.md)
- - [Intro to VNodes](concepts/vnodes.md)
- - [VNodes with RSX, HTML, and NodeFactory](concepts/rsx.md)
- - [RSX in Depth](concepts/rsx_in_depth.md)
- - [Components and Props](concepts/components.md)
- - [Memoization](concepts/memoization.md)
- - [Hooks and Internal State](concepts/hooks.md)
- - [Event handlers](concepts/event_handlers.md)
- - [Global State](concepts/sharedstate.md)
- - [User Input and Controlled Components](concepts/errorhandling.md)
- - [Error handling](concepts/errorhandling.md)
-- [Reference Guide]()
-- [Advanced Guides]()
- - [Custom Renderer]()
- - [LiveView Support]()
- - [Bundling and Distributing]()
+- [Describing the UI](concepts/00-index.md)
+ - [Intro to Elements](concepts/vnodes.md)
+ - [Intro to Components](concepts/components.md)
+ - [Reusing, Importing, and Exporting Components](concepts/exporting_components.md)
+ - [Conditional Rendering](concepts/conditional_rendering.md)
+ - [Lists](concepts/lists.md)
+- [Adding Interactivity](concepts/interactivity.md)
+ - [Event handlers](concepts/event_handlers.md)
+ - [User Input and Controlled Components](concepts/errorhandling.md)
+ - [Lifecycle, updates, and effects](concepts/lifecycles.md)
+- [Managing State](concepts/managing_state.md)
+ - [Hooks and Internal State](concepts/hooks.md)
+ - [Global State](concepts/sharedstate.md)
+ - [Error handling](concepts/errorhandling.md)
+ - [Effects](concepts/effects.md)
+- [Working with Async](concepts/async.md)
+ - [Tasks](concepts/asynctasks.md)
+ - [Suspense](concepts/suspense.md)
+ - [Async Callbacks](concepts/asynccallbacks.md)
+- [Putting it all together](tutorial/index.md)
+ - [New app](tutorial/new_app.md)
+ - [Structuring our app](tutorial/structure.md)
+ - [Defining State](tutorial/state.md)
+ - [Defining Components](tutorial/components.md)
+ - [Styling](tutorial/styling.md)
+ - [Publishing](tutorial/publishing.md)
+- [Topics in Depth](depth/topics.md)
+ - [RSX](depth/rsx.md)
+ - [Components](depth/components.md)
+ - [Props](depth/props.md)
+ - [Memoization](depth/memoization.md)
+ - [Performance](depth/performance.md)
+ - [Testing](depth/testing.md)
+- [Advanced Guides](tutorial/advanced_guides.md)
+ - [Memoization](concepts/memoization.md)
+ - [RSX in Depth](concepts/rsx_in_depth.md)
+ - [Building Elements with NodeFactory](concepts/rsx.md)
+ - [Custom Elements](concepts/custom_elements.md)
+ - [Custom Renderer](concepts/custom_renderer.md)
+ - [Server-side components](concepts/server_side_components.md)
+ - [Bundling and Distributing](concepts/bundline.md)
- [Web]()
- [Getting Started]()
- [Down-casting Nodes]()
@@ -29,33 +54,26 @@
- [Wrapping Web APIs]()
- [Mobile]()
- [Wrapping Web APIs]()
-
-
-
+- [Reference Guide]()
+ - [Anti-patterns]()
+ - [Children]()
+ - [Conditional Rendering]()
+ - [Controlled Inputs]()
+ - [Custom Elements]()
+ - [Empty Components]()
+ - [Error Handling]()
+ - [Fragments]()
+ - [Global CSS]()
+ - [Inline Styles]()
+ - [Iterators]()
+ - [Listeners]()
+ - [Memoization]()
+ - [Node Refs]()
+ - [Spread Pattern]()
+ - [State Management]()
+ - [Suspense]()
+ - [task]()
+ - [Testing]()
-----------
diff --git a/docs/src/concepts/07-state-management.md b/docs/src/advanced-guides/rsx-in-depth.md
similarity index 100%
rename from docs/src/concepts/07-state-management.md
rename to docs/src/advanced-guides/rsx-in-depth.md
diff --git a/docs/src/cli/README.md b/docs/src/cli/README.md
deleted file mode 100644
index bcb71988f..000000000
--- a/docs/src/cli/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Command Line Tool
diff --git a/docs/src/cli/build.md b/docs/src/cli/build.md
deleted file mode 100644
index 6a5f7cb4e..000000000
--- a/docs/src/cli/build.md
+++ /dev/null
@@ -1 +0,0 @@
-# build
diff --git a/docs/src/cli/clean.md b/docs/src/cli/clean.md
deleted file mode 100644
index 0eceb144a..000000000
--- a/docs/src/cli/clean.md
+++ /dev/null
@@ -1 +0,0 @@
-# clean
diff --git a/docs/src/cli/init.md b/docs/src/cli/init.md
deleted file mode 100644
index a6131c10e..000000000
--- a/docs/src/cli/init.md
+++ /dev/null
@@ -1 +0,0 @@
-# init
diff --git a/docs/src/cli/serve.md b/docs/src/cli/serve.md
deleted file mode 100644
index 5293e3f35..000000000
--- a/docs/src/cli/serve.md
+++ /dev/null
@@ -1 +0,0 @@
-# serve
diff --git a/docs/src/cli/test.md b/docs/src/cli/test.md
deleted file mode 100644
index 83c831f0b..000000000
--- a/docs/src/cli/test.md
+++ /dev/null
@@ -1 +0,0 @@
-# test
diff --git a/docs/src/cli/watch.md b/docs/src/cli/watch.md
deleted file mode 100644
index 12ec1aa74..000000000
--- a/docs/src/cli/watch.md
+++ /dev/null
@@ -1 +0,0 @@
-# watch
diff --git a/docs/src/concepts/04-hooks.md b/docs/src/concepts/04-hooks.md
deleted file mode 100644
index 7979ccc6e..000000000
--- a/docs/src/concepts/04-hooks.md
+++ /dev/null
@@ -1,16 +0,0 @@
-```rust
-fn Example<'a>(cx: Context<'a>, props: &()) -> DomTree<'a> {
- let service = use_combubulator(cx);
- let Status { name, pending, count } = service.info();
- html! {
-
-
"Hello, {name}!"
-
"Status: {pending}!"
-
"Count {count}!"
-
-
- }
-}
-```
diff --git a/docs/src/concepts/05-context-api.md b/docs/src/concepts/05-context-api.md
deleted file mode 100644
index 319c4f8ee..000000000
--- a/docs/src/concepts/05-context-api.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Context API
-
-```rust
-// Create contexts available to children
-// Only one context can be associated with any given component
-// This is known as "exposed state". Children can access this context,
-// but will not be automatically subscribed.
-fn ContextCreate(cx: &mut Context<()>) -> DomTree {
- let context = cx.set_context(|| CustomContext::new());
- html! { <> {cx.children()} > }
-}
-
-fn ContextRead(cx: &mut Context<()>) -> DomTree {
- // Panics if context is not available
- let some_cx = cx.get_context::();
- let text = some_cx.select("some_selector");
- html! {
"{text}"
}
-}
-
-fn Subscription(cx: &mut Context<()>) -> DomTree {
- // Open a "port" on the component for actions to trigger a re-evaluation
- let subscription = cx.new_subscription();
-
- // A looping timer - the effect is re-called on every re-evaluation
- use_async_effect(cx, move || async {
- timer::new(2000).await;
- subscription.call();
- }, None);
-
- // A one-shot timer, the deps don't change so the effect only happens once
- use_async_effect_deps(cx, move || async {
- timer::new(2000).await;
- subscription.call();
- }, ());
-}
-
-// Mix subscriptions and context to make a simple Redux
-fn use_global_state(cx: &mut Context<()>) -> T {
- let some_cx = cx.get_context::();
- let component_subscription = cx.new_subscription();
- some_cx.subscribe_component(component_subscription);
- some_cx
-}
-
-```
diff --git a/docs/src/concepts/09-interactivity.md b/docs/src/concepts/09-interactivity.md
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/src/concepts/9901-hello-world.md b/docs/src/concepts/9901-hello-world.md
deleted file mode 100644
index f61ca4f09..000000000
--- a/docs/src/concepts/9901-hello-world.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Hello, World!
-
-Dioxus should look and feel just like writing functional React components. In Dioxus, there are no class components with lifecycles. All state management is done via hooks. This encourages logic reusability and lessens the burden on Dioxus to maintain a non-breaking lifecycle API.
-
-```rust
-#[derive(Properties, PartialEq)]
-struct MyProps {
- name: String
-}
-
-fn Example(cx: Context) -> DomTree {
- cx.render(html! {
-
"Hello {cx.name}!"
- })
-}
-```
-
-For functions to be valid components, they must take the `Context` object which is generic over some properties. The properties parameter must implement the `Properties` trait, which can be automatically derived. Whenever the input properties of a component changes, the function component will be re-ran and a new set of VNodes will be generated.
diff --git a/docs/src/concepts/async.md b/docs/src/concepts/async.md
new file mode 100644
index 000000000..f6c3d103d
--- /dev/null
+++ b/docs/src/concepts/async.md
@@ -0,0 +1 @@
+# Working with Async
diff --git a/docs/src/concepts/asynccallbacks.md b/docs/src/concepts/asynccallbacks.md
new file mode 100644
index 000000000..6fcf65b66
--- /dev/null
+++ b/docs/src/concepts/asynccallbacks.md
@@ -0,0 +1 @@
+# Async Callbacks
diff --git a/docs/src/concepts/asynctasks.md b/docs/src/concepts/asynctasks.md
new file mode 100644
index 000000000..a26beca9a
--- /dev/null
+++ b/docs/src/concepts/asynctasks.md
@@ -0,0 +1 @@
+# Tasks
diff --git a/docs/src/concepts/bundline.md b/docs/src/concepts/bundline.md
new file mode 100644
index 000000000..963cf5490
--- /dev/null
+++ b/docs/src/concepts/bundline.md
@@ -0,0 +1 @@
+# Bundling and Distributing
diff --git a/docs/src/concepts/components.md b/docs/src/concepts/components.md
index 62d0eaf81..81406ff36 100644
--- a/docs/src/concepts/components.md
+++ b/docs/src/concepts/components.md
@@ -1,18 +1,181 @@
-# Components and Props
+# Introduction to Components
-Dioxus should look and feel just like writing functional React components. In Dioxus, there are no class components with lifecycles. All state management is done via hooks. This encourages logic reusability and lessens the burden on Dioxus to maintain a non-breaking lifecycle API.
+In the previous chapter, we learned about Elements and how they can be composed to create a basic User Interface. In this chapter, we'll learn how to group Elements together to form Components.
+
+## What is a component?
+
+In short, a component is a special function that takes an input and outputs a group of Elements. Typically, Components serve a single purpose: group functionality of a User Interface. Much like a function encapsulates some specific computation task, a Component encapsulates some specific rendering task.
+
+### Learning through prior art
+
+Let's take a look at a post on r/rust and see if we can sketch out a component representation.
+
+
+
+This component has a bunch of important information:
+
+- The score
+- The number of comments
+- How long ago it was posted
+- The url short address
+- The title
+- The username of the original poster
+
+If we wanted to sketch out these requirements in Rust, we would start with a struct:
```rust
-#[derive(Properties, PartialEq)]
-struct MyProps {
- name: String
-}
-
-fn Example(cx: Context) -> DomTree {
- html! {
"Hello {cx.cx.name}!"
}
+struct Post {
+ score: i32,
+ comment_count: u32,
+ post_time: Instant,
+ url: String,
+ title: String,
+ original_poster_name: String
}
```
-Here, the `Context` object is used to access hook state, create subscriptions, and interact with the built-in context API. Props, children, and component APIs are accessible via the `Context` object. The functional component macro makes life more productive by inlining props directly as function arguments, similar to how Rocket parses URIs.
+If we look at the layout of the component, we notice quite a few buttons and functionality:
+
+- Upvote/Downvote
+- View comments
+- Share
+- Save
+- Hide
+- Give award
+- Report
+- Crosspost
+- Filter by site
+- View article
+- Visit user
+
+If we included all this functionality in one `rsx!` call, it would be huge! Instead, let's break the post down into some core pieces:
+
+
+
+- **VoteButtons**: Upvote/Downvote
+- **TitleCard**: Title, Filter-By-Url
+- **MetaCard**: Original Poster, Time Submitted
+- **ActionCard**: View comments, Share, Save, Hide, Give award, Report, Crosspost
+
+### Modeling with Dioxus
+
+When designing these components, we can start by sketching out the hierarchy using Dioxus. In general, our "Post" component will simply be comprised of our four sub-components. We would start the process by defining our "Post" component. Our component will take in all of the important data we listed above as part of its input.
+
+Unlike normal functions, Dioxus components must explicitly define a single struct to contain all the inputs. These are commonly called "Properties" (props). Our component will be a combination of these properties and a function to render them.
+
+Our props must implement the `Properties` trait and - if the component does not borrow any data - `PartialEq`. Both of these can be done automatically through derive macros:
+```rust
+#[derive(Properties, PartialEq)]
+struct PostProps {
+ id: Uuid,
+ score: i32,
+ comment_count: u32,
+ post_time: Instant,
+ url: String,
+ title: String,
+ original_poster: String
+}
+```
+
+And our render function:
+```rust
+fn Post((cx, props): Component) -> Element {
+ cx.render(rsx!{
+ div { class: "post-container"
+ VoteButtons {
+ score: props.score,
+ }
+ TitleCard {
+ title: props.title,
+ url: props.url,
+ }
+ MetaCard {
+ original_poster: props.original_poster,
+ post_time: props.post_time,
+ }
+ ActionCard {
+ post_id: props.id
+ }
+ }
+ })
+}
+```
+
+When we render components, we use the traditional Rust struct syntax to declare their properties. Dioxus will automatically call "into" on the property fields, cloning when necessary. Notice how our `Post` component is simply a collection of important smaller components wrapped together in a single container.
+
+Let's take a look at the `VoteButtons` component. For now, we won't include any interactivity - just the rendering the vote buttons and score to the screen.
+
+Most of your Components will look exactly like this: a Props struct and a render function. As covered before, we'll build our User Interface with the `rsx!` macro and HTML tags. However, with components, we must actually "render" our HTML markup. Calling `cx.render` converts our "lazy" `rsx!` structure into an `Element`. Every component must take a tuple of `Context` and `&Props` and return an `Element`.
+```rust
+
+#[derive(PartialEq, Props)]
+struct VoteButtonsProps {
+ score: i32
+}
+
+fn VoteButtons((cx, props): Component) -> Element {
+ cx.render(rsx!{
+ div { class: "votebuttons"
+ div { class: "arrow up" }
+ div { class: "score", "{props.score}"}
+ div { class: "arrow down" }
+ }
+ })
+}
+```
+
+## Borrowing
+
+You can avoid clones using borrowed component syntax. For example, let's say we passed the TitleCard title as an `&str` instead of `String`. In JavaScript, the string would simply be copied by reference - none of the contents would be copied, but rather the reference to the string's contents are copied. In Rust, this would be similar to calling `clone` on `Rc`.
+
+Because we're working in Rust, we can choose to either use `Rc`, clone `Title` on every re-render of `Post`, or simply borrow it. In most cases, you'll just want to let `Title` be cloned.
+
+To enable borrowed values for your component, we need to add a lifetime to let the Rust compiler know that the output `Element` borrows from the component's props.
+
+```rust
+#[derive(Properties)]
+struct TitleCardProps<'a> {
+ title: &'a str,
+}
+
+fn TitleCard<'a>((cx, props): Component<'a, TitleCardProps>) -> Element<'a> {
+ cx.render(rsx!{
+ h1 { "{props.title}" }
+ })
+}
+```
+
+## The `Context` object
+
+Though very similar with React, Dioxus is different in a few ways. Most notably, React components will not have a `Context` parameter in the component declaration.
+
+Have you ever wondered how the `useState()` call works in React without a `this` object to actually store the state?
+
+React uses global variables to store this information which must be carefully managed, especially in environments with multiple React roots - like the server.
+
+```javascript
+function Component({}) {
+ let [state, set_state] = useState(10);
+}
+```
+
+
+Because Dioxus needs to work with the rules of Rust, we need to provide a way for the component to do some internal bookkeeping. That's what the `Context` object is: a place for the component to store state, manage listeners, and allocate elements. Advanced users of Dioxus will want to learn how to properly leverage the `Context` object to build robust, performant extensions for Dioxus.
+
+```rust
+fn Post((cx /* <-- our Context object*/, props): Component) -> Element {
+ cx.render(rsx!{ })
+}
+```
+
+## Moving forward
+
+Next chapter, we'll talk about composing Elements and Components across files to build a larger Dioxus App.
+
+For more references on components, make sure to check out:
+
+- [Components in depth]()
+- [Lifecycles]()
+- [The Context object]()
+- [Optional Prop fields]()
-The final output of components must be a tree of VNodes. We provide an html macro for using JSX-style syntax to write these, though, you could use any macro, DSL, templating engine, or the constructors directly.
diff --git a/docs/src/concepts/conditional_rendering.md b/docs/src/concepts/conditional_rendering.md
new file mode 100644
index 000000000..b6cea82dd
--- /dev/null
+++ b/docs/src/concepts/conditional_rendering.md
@@ -0,0 +1 @@
+# Conditional Rendering
diff --git a/docs/src/concepts/custom_elements.md b/docs/src/concepts/custom_elements.md
new file mode 100644
index 000000000..3b24ad9fe
--- /dev/null
+++ b/docs/src/concepts/custom_elements.md
@@ -0,0 +1 @@
+# Custom Elements
diff --git a/docs/src/concepts/custom_renderer.md b/docs/src/concepts/custom_renderer.md
new file mode 100644
index 000000000..577b4f5d3
--- /dev/null
+++ b/docs/src/concepts/custom_renderer.md
@@ -0,0 +1 @@
+# Custom Renderer
diff --git a/docs/src/concepts/effects.md b/docs/src/concepts/effects.md
new file mode 100644
index 000000000..7b8ba3e3f
--- /dev/null
+++ b/docs/src/concepts/effects.md
@@ -0,0 +1 @@
+# Effects
diff --git a/docs/src/concepts/exporting_components.md b/docs/src/concepts/exporting_components.md
new file mode 100644
index 000000000..4dac4a339
--- /dev/null
+++ b/docs/src/concepts/exporting_components.md
@@ -0,0 +1,291 @@
+
+# Reusing, Importing, and Exporting Components
+
+As your application grows in size, you'll want to start breaking your UI into components and, eventually, different files. This is a great idea to encapsulate functionality of your UI and scale your team.
+
+Let's say our app looks something like this:
+
+```shell
+├── Cargo.toml
+└── src
+ └── main.rs
+```
+
+```rust
+// main.rs
+use dioxus::prelude::*;
+
+fn main() {
+ dioxus::desktop::launch(App, |c| c);
+}
+
+fn App((cx, props): Component<()>) -> Element {}
+
+#[derive(PartialEq, Props)]
+struct PostProps{}
+fn Post((cx, props): Component) -> Element {}
+
+#[derive(PartialEq, Props)]
+struct VoteButtonsProps {}
+fn VoteButtons((cx, props): Component) -> Element {}
+
+#[derive(PartialEq, Props)]
+struct TitleCardProps {}
+fn TitleCard((cx, props): Component) -> Element {}
+
+#[derive(PartialEq, Props)]
+struct MetaCardProps {}
+fn MetaCard((cx, props): Component) -> Element {}
+
+#[derive(PartialEq, Props)]
+struct ActionCardProps {}
+fn ActionCard((cx, props): Component) -> Element {}
+```
+
+That's a lot of components for one file! We've successfully refactored our app into components, but we should probably start breaking it up into a file for each component.
+
+## Breaking into different files
+
+Fortunately, Rust has a built-in module system that's much cleaner than what you might be used to in JavaScript. Because `VoteButtons`, `TitleCard`, `MetaCard`, and `ActionCard` all belong to the `Post` component, let's put them all in a folder together called "post". We'll make a file for each component and move the props and render function.
+
+```rust
+// src/post/action.rs
+
+use dioxus::prelude::*;
+
+#[derive(PartialEq, Props)]
+struct ActionCardProps {}
+fn ActionCard((cx, props): Component) -> Element {}
+```
+
+We should also create a `mod.rs` file in the `post` folder so we can use it from our `main.rs`. Our `Post` component and its props will go into this file.
+
+```shell
+├── Cargo.toml
+└── src
+ ├── main.rs
+ └── post
+ ├── vote.rs
+ ├── title.rs
+ ├── meta.rs
+ ├── action.rs
+ └── mod.rs
+```
+
+
+
+In our `main.rs`, we'll want to declare the `post` module so we can access our `Post` component.
+
+```rust
+// main.rs
+use dioxus::prelude::*;
+
+fn main() {
+ dioxus::desktop::launch(App, |c| c);
+}
+
+mod post;
+
+fn App((cx, props): Component<()>) -> Element {
+ cx.render(rsx!{
+ post::Post {
+ id: Uuid::new_v4(),
+ score: 10,
+ comment_count: 10,
+ post_time: std::Instant::now(),
+ url: "example".to_string(),
+ title: "Title".to_string(),
+ original_poster: "me".to_string()
+ }
+ })
+}
+```
+
+If you tried to build this app right now, you'll get an error message saying that `Post is private, trying changing it to public`. This is because we haven't properly exported our component! To fix this, we need to make sure both the Props and Component are declared as "public":
+
+```rust
+// src/post/mod.rs
+
+use dioxus::prelude::*;
+
+#[derive(PartialEq, Props)]
+pub struct PostProps {}
+pub fn Post((cx, props): Component) -> Element {}
+```
+
+While we're here, we also need to make sure each of our subcomponents are included as modules and exported.
+
+Our "post/mod.rs" file will eventually look like this:
+
+```rust
+use dioxus::prelude::*;
+
+mod vote;
+mod title;
+mod meta;
+mod action;
+
+#[derive(Properties, PartialEq)]
+pub struct PostProps {
+ id: uuid::Uuid,
+ score: i32,
+ comment_count: u32,
+ post_time: std::time::Instant,
+ url: String,
+ title: String,
+ original_poster: String
+}
+
+pub fn Post((cx, props): Component) -> Element {
+ cx.render(rsx!{
+ div { class: "post-container"
+ vote::VoteButtons {
+ score: props.score,
+ }
+ title::TitleCard {
+ title: props.title,
+ url: props.url,
+ }
+ meta::MetaCard {
+ original_poster: props.original_poster,
+ post_time: props.post_time,
+ }
+ action::ActionCard {
+ post_id: props.id
+ }
+ }
+ })
+}
+```
+
+
+Ultimately, including and exporting components is governed by Rust's module system. [The Rust book is a great resource to learn about these concepts in greater detail.](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html)
+
+## Final structure:
+
+```shell
+├── Cargo.toml
+└── src
+ ├── main.rs
+ └── post
+ ├── vote.rs
+ ├── title.rs
+ ├── meta.rs
+ ├── action.rs
+ └── mod.rs
+```
+
+```rust
+// main.rs:
+use dioxus::prelude::*;
+
+fn main() {
+ dioxus::desktop::launch(App, |c| c);
+}
+
+mod post;
+
+fn App((cx, props): Component<()>) -> Element {
+ cx.render(rsx!{
+ post::Post {
+ id: Uuid::new_v4(),
+ score: 10,
+ comment_count: 10,
+ post_time: std::Instant::now(),
+ url: "example".to_string(),
+ title: "Title".to_string(),
+ original_poster: "me".to_string()
+ }
+ })
+}
+```
+
+
+```rust
+// src/post/mod.rs
+use dioxus::prelude::*;
+
+mod vote;
+mod title;
+mod meta;
+mod action;
+
+#[derive(Properties, PartialEq)]
+pub struct PostProps {
+ id: uuid::Uuid,
+ score: i32,
+ comment_count: u32,
+ post_time: std::time::Instant,
+ url: String,
+ title: String,
+ original_poster: String
+}
+
+pub fn Post((cx, props): Component) -> Element {
+ cx.render(rsx!{
+ div { class: "post-container"
+ vote::VoteButtons {
+ score: props.score,
+ }
+ title::TitleCard {
+ title: props.title,
+ url: props.url,
+ }
+ meta::MetaCard {
+ original_poster: props.original_poster,
+ post_time: props.post_time,
+ }
+ action::ActionCard {
+ post_id: props.id
+ }
+ }
+ })
+}
+```
+
+```rust
+// src/post/vote.rs
+use dioxus::prelude::*;
+
+#[derive(PartialEq, Props)]
+pub struct VoteButtonsProps {}
+pub fn VoteButtons((cx, props): Component) -> Element {}
+```
+
+```rust
+// src/post/title.rs
+use dioxus::prelude::*;
+
+#[derive(PartialEq, Props)]
+pub struct TitleCardProps {}
+pub fn TitleCard((cx, props): Component) -> Element {}
+```
+
+```rust
+// src/post/meta.rs
+use dioxus::prelude::*;
+
+#[derive(PartialEq, Props)]
+pub struct MetaCardProps {}
+pub fn MetaCard((cx, props): Component) -> Element {}
+```
+
+```rust
+// src/post/action.rs
+use dioxus::prelude::*;
+
+#[derive(PartialEq, Props)]
+pub struct ActionCardProps {}
+pub fn ActionCard((cx, props): Component) -> Element {}
+```
+
+## Moving forward
+
+Next chapter, we'll start to add use code to hide and show Elements with conditional rendering.
+
+For more reading on components:
+
+- [Components in depth]()
+- [Lifecycles]()
+- [The Context object]()
+- [Optional Prop fields]()
diff --git a/docs/src/concepts/interactivity.md b/docs/src/concepts/interactivity.md
new file mode 100644
index 000000000..5e99d9853
--- /dev/null
+++ b/docs/src/concepts/interactivity.md
@@ -0,0 +1 @@
+# Adding Interactivity
diff --git a/docs/src/concepts/lifecycles.md b/docs/src/concepts/lifecycles.md
new file mode 100644
index 000000000..07a02f4ea
--- /dev/null
+++ b/docs/src/concepts/lifecycles.md
@@ -0,0 +1 @@
+# Lifecycle, updates, and effects
diff --git a/docs/src/concepts/managing_state.md b/docs/src/concepts/managing_state.md
new file mode 100644
index 000000000..931d66c2f
--- /dev/null
+++ b/docs/src/concepts/managing_state.md
@@ -0,0 +1 @@
+# Managing State
diff --git a/docs/src/concepts/server_side_components.md b/docs/src/concepts/server_side_components.md
new file mode 100644
index 000000000..a8c4f6182
--- /dev/null
+++ b/docs/src/concepts/server_side_components.md
@@ -0,0 +1 @@
+# Server-side components
diff --git a/docs/src/concepts/suspense.md b/docs/src/concepts/suspense.md
new file mode 100644
index 000000000..f6d8c7e98
--- /dev/null
+++ b/docs/src/concepts/suspense.md
@@ -0,0 +1 @@
+# Suspense
diff --git a/docs/src/concepts/vnodes.md b/docs/src/concepts/vnodes.md
index f1de32e41..23092cfd6 100644
--- a/docs/src/concepts/vnodes.md
+++ b/docs/src/concepts/vnodes.md
@@ -1,64 +1,153 @@
-# VNodes and Elements
+# Declaring your first UI with Elements
-At the heart of Dioxus is the concept of an "element" - a container that can have children, properties, event handlers, and other important attributes. Dioxus only knows how to render the `VNode` data structure - an Enum variant of an Element, Text, Components, Fragments, and Anchors.
+Every user interface you've ever used is just a symphony of tiny widgets working together to abstract over larger complex functions. In Dioxus, we call these tiny widgets "Elements." Using Components, you can easily compose Elements into larger groups to form even larger structures: Apps.
-Because Dioxus is meant for the Web and uses WebView as a desktop and mobile renderer, almost all elements in Dioxus share properties with their HTML counterpart. When we declare our elements, we'll do so using HTML semantics:
+Because Dioxus is mostly used with HTML/CSS renderers, the default Element "collection" is HTML. Provided the `html` feature is not disabled, we can declare Elements using the `rsx!` macro:
```rust
+#use dioxus::prelude::*;
+rsx!(
+ div {}
+)
+```
+As you might expect, we can render this call using Dioxus-SSR to produce valid HTML:
+
+```rust
+#use dioxus::prelude::*;
+dioxus::ssr::render_lazy(rsx!(
+ div {}
+))
+```
+
+Produces:
+```html
+
+```
+
+We can construct any valid HTML tag with the `tag {}` pattern and expect the resulting HTML structure to resemble our declaration.
+## Composing Elements
+
+Of course, we need more complex structures to make our apps actually useful! Just like HTML, the `rsx!` macro lets us nest Elements inside of each other.
+
+```rust
+#use dioxus::prelude::*;
rsx!(
div {
- "hello world"
+ h1 {}
+ h2 {}
+ p {}
+ }
+)
+```
+As you might expect, the generated HTML for this structure would look like:
+```html
+
+
+
+
+
+```
+
+With the default configuration, any Element defined within the `dioxus-html` crate can be declared in this way. To create your own new elements, see the `Custom Elements` Advanced Guide.
+
+## Text Elements
+
+Dioxus also supports a special type of Element: Text. Text Elements do not accept children, but rather just string literals denoted with double quotes.
+
+```rust
+rsx! (
+ "hello world"
+)
+```
+
+Text Elements can be composed within other Elements:
+```rust
+rsx! (
+ div {
+ h1 { "hello world" }
+ p { "Some body content" }
}
)
```
-As you would expect, this snippet would generate a simple hello-world div. In fact, we can render these nodes directly with the SSR crate:
+Text can also be formatted with any value that implements `Display`. We use [f-string formatting](https://docs.rs/fstrings/0.2.3/fstrings/) - a "coming soon" feature for stable Rust that is familiar for Python and JavaScript users:
```rust
-dioxus::ssr::render_lazy(rsx!(
+let name = "Bob";
+rsx! ( "hello {name}" )
+```
+
+Unfortunately, you cannot drop in arbitrary expressions directly into the string literal. In the cases where we need to compute a complex value, we'll want to use `format_args!` directly. Due to specifics of how the `rsx!` macro (we'll cover later), our call to `format_args` must be contained within curly braces *and* square braces.
+
+```rust
+rsx!( {[format_args!("Hello {}", if enabled { "Jack" } else { "Bob" } )]} )
+```
+
+This is different from React's way of generating arbitrary markup but fits within idiomatic Rust.
+
+Typically, with Dioxus, you'll just want to compute your substrings outside of the `rsx!` call:
+
+```rust
+let name = if enabled { "Jack" } else { "Bob" };
+rsx! ( "hello {name}" )
+```
+
+## Attributes
+
+Every Element in your User Interface will have some sort of properties that the renderer will use when drawing to the screen. These might inform the renderer if the component should be hidden, what its background color should be, or to give it a specific name or ID.
+
+To do this, we use the familiar struct-style syntax that Rust provides. Commas are optional:
+
+```rust
+rsx!(
div {
- "hello world"
+ hidden: true,
+ background_color: "blue",
+ class: "card color-{mycolor}"
}
-))
+)
```
-And produce the corresponding html structure:
-```html
-
hello world
+Each field is defined as a method on the element in the `dioxus-html` crate. This prevents you from misspelling a field name and lets us provide inline documentation. When you need to use a field not defined as a method, you have two options:
+
+1) file an issue if the attribute _should_ be enabled
+2) add a custom attribute on-the-fly
+
+To use custom attributes, simply put the attribute name in quotes followed by a colon:
+
+```rust
+rsx!(
+ div {
+ "customAttr": "important data here"
+ }
+)
```
-Our structure declared above is made of two variants of the `VNode` data structure:
-- A VElement with a tag name of `div`
-- A VText with contents of `"hello world"`
+Note: the name of the custom attribute must match exactly what you want the renderer to output. All attributes defined as methods in `dioxus-html` follow the snake_case naming convention. However, they internally translate their snake_case convention to HTML's camelCase convention.
-## All the VNode types
+## Listeners
-VNodes can be any of:
-- **Element**: a container with a tag name, namespace, attributes, children, and event listeners
-- **Text**: bump allocated text derived from string formatting
-- **Fragments**: a container of elements with no parent
-- **Suspended**: a container for nodes that aren't yet ready to be rendered
-- **Anchor**: a special type of node that is only available when fragments have no children
+Listeners are a special type of Attribute that only accept functions. Listeners let us attach functionality to our Elements by running a provided closure whenever the specified Listener is triggered.
-In practice, only elements and text can be initialized directly while other node types can only be created through hooks or NodeFactory methods.
+We'll cover listeners in more depth in the Listeners chapter, but for now, just know that every listener must start with the `on` keyword and can accept either a closure or an expression wrapped in curly braces.
-## Bump Arena Allocation
-
-To speed up the process of building our elements and text, Dioxus uses a special type of memory allocator tuned for large batches of small allocations called a Bump Arena. We use the `bumpalo` allocator which was initially developed for Dioxus' spiritual predecessor: `Dodrio.`
-
-- Bumpalo: [https://github.com/fitzgen/bumpalo](https://github.com/fitzgen/bumpalo)
-- Dodrio: [https://github.com/fitzgen/dodrio](https://github.com/fitzgen/dodrio)
-
-In other frontend frameworks for Rust, nearly every string is allocated using the global allocator. This means that strings in Rust do not benefit from the immutable string interning optimizations that JavaScript engines employ. By using a smaller, faster, more limited allocator, we can increase framework performance, bypassing even the naive wasm-bindgen benchmarks for very quick renders.
-
-It's important to note that VNodes are not `'static` - the VNode definition has a lifetime attached to it:
-
-```rust, ignore
-enum VNode<'bump> {
- VElement { tag: &'static str, children: &'bump [VNode<'bump>] },
- VText { content: &'bump str },
- // other VNodes ....
-}
+```rust
+rsx!(
+ div {
+ onclick: move |_| {}
+ onmouseover: {handler},
+ }
+)
```
-Because VNodes use a bump allocator as their memory backing, they can only be created through the `NodeFactory` API - which we'll cover in the next chapter. This particular detail is important to understand because "rendering" VNodes produces a lifetime attached to the bump arena - which must be explicitly declared when dealing with components that borrow data from their parents.
+## Moving On
+
+This chapter just scratches the surface on how Elements can be defined.
+
+We learned:
+- Elements are the basic building blocks of User Interfaces
+- Elements can contain other elements
+- Elements can either be a named container or text
+- Some Elements have properties that the renderer can use to draw the UI to the screen
+
+Next, we'll compose Elements together to form components.
diff --git a/docs/src/depth/components.md b/docs/src/depth/components.md
new file mode 100644
index 000000000..0eac4eb52
--- /dev/null
+++ b/docs/src/depth/components.md
@@ -0,0 +1 @@
+# Components
diff --git a/docs/src/depth/memoization.md b/docs/src/depth/memoization.md
new file mode 100644
index 000000000..3ed477f68
--- /dev/null
+++ b/docs/src/depth/memoization.md
@@ -0,0 +1 @@
+# Memoization
diff --git a/docs/src/depth/performance.md b/docs/src/depth/performance.md
new file mode 100644
index 000000000..6babb314a
--- /dev/null
+++ b/docs/src/depth/performance.md
@@ -0,0 +1 @@
+# Performance
diff --git a/docs/src/depth/props.md b/docs/src/depth/props.md
new file mode 100644
index 000000000..d4b69bda7
--- /dev/null
+++ b/docs/src/depth/props.md
@@ -0,0 +1 @@
+# Props
diff --git a/docs/src/depth/rsx.md b/docs/src/depth/rsx.md
new file mode 100644
index 000000000..1f349a7ef
--- /dev/null
+++ b/docs/src/depth/rsx.md
@@ -0,0 +1 @@
+# RSX
diff --git a/docs/src/depth/testing.md b/docs/src/depth/testing.md
new file mode 100644
index 000000000..f00b526a9
--- /dev/null
+++ b/docs/src/depth/testing.md
@@ -0,0 +1 @@
+# Testing
diff --git a/docs/src/depth/topics.md b/docs/src/depth/topics.md
new file mode 100644
index 000000000..0cf65091f
--- /dev/null
+++ b/docs/src/depth/topics.md
@@ -0,0 +1 @@
+# Topics in Depth
diff --git a/docs/src/format/README.md b/docs/src/format/README.md
deleted file mode 100644
index a095177ba..000000000
--- a/docs/src/format/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Format
diff --git a/docs/src/format/configuration/README.md b/docs/src/format/configuration/README.md
deleted file mode 100644
index a025a48b1..000000000
--- a/docs/src/format/configuration/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Configuration
diff --git a/docs/src/format/configuration/environment-variables.md b/docs/src/format/configuration/environment-variables.md
deleted file mode 100644
index 1d535f22b..000000000
--- a/docs/src/format/configuration/environment-variables.md
+++ /dev/null
@@ -1 +0,0 @@
-# Environment Variables
diff --git a/docs/src/format/configuration/general.md b/docs/src/format/configuration/general.md
deleted file mode 100644
index 23d342b49..000000000
--- a/docs/src/format/configuration/general.md
+++ /dev/null
@@ -1 +0,0 @@
-# General
diff --git a/docs/src/format/configuration/preprocessors.md b/docs/src/format/configuration/preprocessors.md
deleted file mode 100644
index eafd89490..000000000
--- a/docs/src/format/configuration/preprocessors.md
+++ /dev/null
@@ -1 +0,0 @@
-# Preprocessors
diff --git a/docs/src/format/configuration/renderers.md b/docs/src/format/configuration/renderers.md
deleted file mode 100644
index 3d174b89a..000000000
--- a/docs/src/format/configuration/renderers.md
+++ /dev/null
@@ -1 +0,0 @@
-# Renderers
diff --git a/docs/src/format/mathjax.md b/docs/src/format/mathjax.md
deleted file mode 100644
index 41c57bff1..000000000
--- a/docs/src/format/mathjax.md
+++ /dev/null
@@ -1 +0,0 @@
-# MathJax Support
diff --git a/docs/src/format/mdbook.md b/docs/src/format/mdbook.md
deleted file mode 100644
index 13bd6bc23..000000000
--- a/docs/src/format/mdbook.md
+++ /dev/null
@@ -1 +0,0 @@
-# mdBook-specific features
diff --git a/docs/src/format/summary.md b/docs/src/format/summary.md
deleted file mode 100644
index 746e7ad06..000000000
--- a/docs/src/format/summary.md
+++ /dev/null
@@ -1 +0,0 @@
-# SUMMARY.md
diff --git a/docs/src/format/theme/README.md b/docs/src/format/theme/README.md
deleted file mode 100644
index dbe490157..000000000
--- a/docs/src/format/theme/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Theme
diff --git a/docs/src/format/theme/editor.md b/docs/src/format/theme/editor.md
deleted file mode 100644
index 068f68b3c..000000000
--- a/docs/src/format/theme/editor.md
+++ /dev/null
@@ -1 +0,0 @@
-# Editor
diff --git a/docs/src/format/theme/index-hbs.md b/docs/src/format/theme/index-hbs.md
deleted file mode 100644
index 21b912727..000000000
--- a/docs/src/format/theme/index-hbs.md
+++ /dev/null
@@ -1 +0,0 @@
-# index.hbs
diff --git a/docs/src/format/theme/syntax-highlighting.md b/docs/src/format/theme/syntax-highlighting.md
deleted file mode 100644
index 5a52c1080..000000000
--- a/docs/src/format/theme/syntax-highlighting.md
+++ /dev/null
@@ -1 +0,0 @@
-# Syntax highlighting
diff --git a/docs/src/gettingstarted/fromjs.md b/docs/src/gettingstarted/fromjs.md
deleted file mode 100644
index 64b1dd56f..000000000
--- a/docs/src/gettingstarted/fromjs.md
+++ /dev/null
@@ -1,37 +0,0 @@
-### Immutability by default?
-
----
-
-Rust, like JS and TS, supports both mutable and immutable data. With JS, `const` would be used to signify immutable data, while in rust, the absence of `mut` signifies immutable data.
-
-Mutability:
-
-```rust
-let mut val = 10; // rust
-let val = 10; // js
-```
-
-Immutability
-
-```rust
-let val = 10; // rust
-const val = 10; // js
-```
-
-However, `const` in JS does not prohibit you from modify the value itself only disallowing assignment. In Rust, immutable **is immutable**. You _never_ have to work about accidentally mutating data; mutating immutable data in Rust requires deliberate advanced data structures that you won't find in your typical frontend code.
-
-## How do strings work?
-
----
-
-In rust, we have `&str`, `&'static str` `String`, and `Rc`. It's a lot, yes, and it might be confusing at first. But it's actually not too bad.
-
-In Rust, UTF-8 is supported natively, allowing for emoji and extended character sets (like Chinese and Arabic!) instead of the typical ASCII. The primitive `str` can be seen as a couple of UTF-8 code points squished together with a dynamic size. Because this size is variable (not known at compile time for any single character), we reference an array of UTF-8 code points as `&str`. Essentially, we're referencing (the & symbol) some dynamic `str` (a collection of UTF-8 points).
-
-For text encoded directly in your code, this collection of UTF-8 code points is given the `'static` reference lifetime - essentially meaning the text can be safely referenced for the entire runtime of your program. Contrast this with JS, where a string will only exist for as long as code references it before it gets cleaned up by the garbage collector.
-
-For text that needs to have characters added, removed, sorted, uppercased, formatted, accessed for mutation, etc, Rust has the `String` type, which is essentially just a dynamically sized `str`. In JS, if you add a character to your string, you actually create an entirely new string (completely cloning the old one first). In Rust, you can safely added characters to strings _without_ having to clone first, making string manipulation in Rust very efficient.
-
-Finally, we have `Rc`. This is essentially Rust's version of JavaScript's `string`. In JS, whenever you pass a `string` around (and don't mutate it), you don't actually clone it, but rather just increment a counter that says "this code is using this string." This counter prevents the garbage collector from deleting the string before your code is done using it. Only when all parts of your code are done with the string, will the string be deleted. `Rc` works exactly the same way in Rust, but requires a deliberate `.clone()` to get the same behavior. In most instances, Dioxus will automatically do this for you, saving the trouble of having to `clone` when you pass an `Rc` into child components. `Rc` is typically better than `String` for Rust - it allows cheap sharing of strings, and through `make_mut` you can always produce your own mutable copy for modifying. You might not see `Rc` in other Rust libraries as much, but you will see it in Dioxus due to Dioxus' aggressive memoization and focus on efficiency and performance.
-
-If you run into issues with `&str`, `String`, `Rc`, just try cloning and `to_string` first. For the vast majority of apps, the slight performance hit will be unnoticeable. Once you get better with Strings, it's very easy to go back and remove all the clones for more efficient alternatives, but you will likely never need to.
diff --git a/docs/src/gettingstarted/webapps.md b/docs/src/gettingstarted/webapps.md
deleted file mode 100644
index 5474fc3b9..000000000
--- a/docs/src/gettingstarted/webapps.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Choose your architecture type:
-- Static Pages
-- Server-side-rendering
-- Pure SPA
-- Hybrid SPA (liveview)
-
-
-## Static Pages
-You'll want the TextRenderer crate.
-Use it imperatively by passing props and rendering to string.
-
-## Server-side-rendering
-You'll want the TextRenderer crate.
-Serve it with a dedicated middleware crate.
-
-## Pure SPA
-You'll want the WebSys Crate.
-
-## Hybrid SPA (hydration)
-You'll want the WebSys Crate
-You'll want the TextRenderer crate
-
-
-## Hybrid SPA (liveview)
-You'll want the WebSys crate.
-You'll want the Liveview crate.
diff --git a/docs/src/hello_world.md b/docs/src/hello_world.md
index 5bcc51939..f7fcee42e 100644
--- a/docs/src/hello_world.md
+++ b/docs/src/hello_world.md
@@ -4,6 +4,8 @@ Let's put together a simple "hello world" to get acquainted with Dioxus. The Dio
This demo will build a simple desktop app. Check out the platform-specific setup guides on how to port your app to different targets.
+### A new project with Cargo
+
First, let's start a new project. Rust has the concept of executables and libraries. Executables have a `main.rs` and libraries have `lib.rs`. A project may have both. Our `hello world` will be an executable - we expect our app to launch when we run it! Cargo provides this for us:
```shell
@@ -65,6 +67,8 @@ edition = "2018"
```
+### Adding Dioxus as a dependency
+
To use the Dioxus library, we'll want to add the most recent version of `Dioxus` to our crate. If you have `cargo edit` installed, simply call:
```shell
@@ -75,6 +79,8 @@ It's very important to add `dioxus` with the `desktop` feature for this example.
If you plan to develop extensions for the `Dioxus` ecosystem, please use the `dioxus` crate with the `core` feature to limit the amount of dependencies your project brings in.
+### Our first app
+
Now, let's edit our `main.rs` file:
```rust
@@ -85,14 +91,18 @@ fn main() {
dioxus::desktop::start(App, |c| c);
}
-static App: FC<()> = |(cx, props)| {
+fn App((cx, props): Component<()>) -> Element {
cx.render(rsx! (
div { "Hello, world!" }
))
-};
+}
```
-Let's dissect our example a bit.
+At this point, you could call `cargo run` and be greeted with a simple `Hello, World!` screen:
+
+
+
+### Dissecting our example
This bit of code imports everything from the the `prelude` module. This brings into scope the right traits, types, and macros needed for working with Dioxus.
@@ -100,7 +110,7 @@ This bit of code imports everything from the the `prelude` module. This brings i
use dioxus::prelude::*;
```
-This initialization code launches a Tokio runtime on a helper thread - where your code will run, and then the WebView on the main-thread. Due to platform requirements, the main thread is blocked by this call.
+This initialization code launches a Tokio runtime on a helper thread where your code will run. Then, the WebView renderer will be launched on the main-thread. Due to platform requirements, the main thread is blocked by your app's event loop.
```rust
fn main() {
@@ -108,48 +118,40 @@ fn main() {
}
```
-Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Option`.
+Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Element`.
```rust
-static App: FC<()> = |(cx, props)| {
+fn App((cx, props): Component<()>) -> Element {
+ cx.render(rsx! {
+ div { "Hello, world!" }
+ })
+}
+```
+In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax:
+
+```rust
+fn App<'a>(cx: Component<'a, ()>) -> Element<'a> {
+ cx.render(rsx! {
+ div { "Hello, world!" }
+ })
+}
+```
+
+Writing `fn App((cx, props): Component<()>) -> Element {` might become tedious. Rust will also let you write functions as static closures, but these types of Components cannot have props that borrow data.
+```rust
+static App: Fc<()> = |(cx, props)| {
cx.render(rsx! {
div { "Hello, world!" }
})
};
```
-The closure `FC<()>` syntax is identical to the function syntax, but with lifetimes managed for you. In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax:
+### The `Context` object
-```rust
-fn App<'a>(cx: Context<'a>, props: &'a ()) -> DomTree<'a> {
- cx.render(rsx! {
- div { "Hello, world!" }
- })
-}
-```
+In React, you'll want to store data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering. In Dioxus, you are given an explicit `Context` object to control how the component renders and stores data.
-
-In React, you'll save data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering. In Dioxus, you are given an explicit `Context` object to control how the component renders and stores data.
+### The `rsx!` macro
Next, we're greeted with the `rsx!` macro. This lets us add a custom DSL for declaratively building the structure of our app. The semantics of this macro are similar to that of JSX and HTML, though with a familiar Rust-y interface. The `html!` macro is also available for writing components with a JSX/HTML syntax.
The `rsx!` macro is lazy: it does not immediately produce elements or allocates, but rather builds a closure which can be rendered with `cx.render`.
-
-Now, let's launch our app:
-
-```shell
-$ cargo run
-```
-
-Huzzah! We have a simple app.
-
-
-
-If we wanted to golf a bit, we can shrink our hello-world even smaller:
-
-```rust
-fn main() {
- static App: FC<()> = |(cx, props)| rsx!(cx, div {"hello world!"});
- dioxus::web::start(App, |c| c);
-}
-```
diff --git a/docs/src/images/helloworld.png b/docs/src/images/helloworld.png
new file mode 100644
index 000000000..c10ebdf46
Binary files /dev/null and b/docs/src/images/helloworld.png differ
diff --git a/docs/src/images/reddit.png b/docs/src/images/reddit.png
new file mode 100644
index 000000000..2277db264
Binary files /dev/null and b/docs/src/images/reddit.png differ
diff --git a/docs/src/images/reddit_post.png b/docs/src/images/reddit_post.png
new file mode 100644
index 000000000..6eccf39b2
Binary files /dev/null and b/docs/src/images/reddit_post.png differ
diff --git a/docs/src/images/reddit_post_components.png b/docs/src/images/reddit_post_components.png
new file mode 100644
index 000000000..f9548a0fa
Binary files /dev/null and b/docs/src/images/reddit_post_components.png differ
diff --git a/docs/src/platforms/00-index.md b/docs/src/platforms/00-index.md
deleted file mode 100644
index ee7fc2cdb..000000000
--- a/docs/src/platforms/00-index.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Welcome to Dioxus!
-
-## Running Examples
-
-We use the dedicated `dioxus-cli` to build and test dioxus web-apps. This can run examples, tests, build web workers, launch development servers, bundle, and more. It's general purpose, but currently very tailored to Dioxus for liveview and bundling. If you've not used it before, `cargo install --path packages/dioxus-cli` will get it installed. This CLI tool should feel like using `cargo` but with 1st party support for assets, bundling, and other important dioxus-specific features.
-
-Alternatively, `trunk` works but can't run examples.
-
-- tide_ssr: Handle an HTTP request and return an html body using the html! macro. `cargo run --example tide_ssr`
-- doc_generator: Use dioxus SSR to generate the website and docs. `cargo run --example doc_generator`
-- fc_macro: Use the functional component macro to build terse components. `cargo run --example fc_macro`
-- hello_web: Start a simple Wasm app. Requires a web packer like dioxus-cli or trunk `cargo run --example hello`
-- router: `cargo run --example router`
-- tide_ssr: `cargo run --example tide_ssr`
-- webview: Use liveview to bridge into a webview context for a simple desktop application. `cargo run --example webview`
-- twitter-clone: A full-featured Twitter clone showcasing dioxus-liveview, state management patterns, and hooks. `cargo run --example twitter`
diff --git a/docs/src/platforms/01-ssr.md b/docs/src/platforms/01-ssr.md
deleted file mode 100644
index 4880fd321..000000000
--- a/docs/src/platforms/01-ssr.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# The Server-Side-Rendering Guide
-
-This guide will help you build your first app that leverages server-side-rendering to display the user interface.
diff --git a/docs/src/platforms/02-wasm.md b/docs/src/platforms/02-wasm.md
deleted file mode 100644
index 0ffabecd1..000000000
--- a/docs/src/platforms/02-wasm.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# The Wasm Guide
-
-This guide will help you build your first app that leverages Wasm in the browser to display the user interface.
diff --git a/docs/src/platforms/03-desktop.md b/docs/src/platforms/03-desktop.md
deleted file mode 100644
index 39ac86d97..000000000
--- a/docs/src/platforms/03-desktop.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# The Desktop Guide
-
-This guide will help you build your first app that leverages webview for desktop to display the user interface.
diff --git a/docs/src/platforms/04-concurrency.md b/docs/src/platforms/04-concurrency.md
deleted file mode 100644
index c1b2fb12d..000000000
--- a/docs/src/platforms/04-concurrency.md
+++ /dev/null
@@ -1,21 +0,0 @@
-## Concurrency
-
-In Dioxus, VNodes are asynchronous and can their rendering can be paused at any time by awaiting a future. Hooks can combine this functionality with the Context and Subscription APIs to craft dynamic and efficient user experiences.
-
-```rust
-fn user_data(cx: Context<()>) -> DomTree {
- // Register this future as a task
- use_suspense(cx, async {
- // Continue on with the component as usual, waiting for data to arrive
- let Profile { name, birthday, .. } = fetch_data().await;
- html! {
-
- }
- })
-}
-```
-
-Asynchronous components are powerful but can also be easy to misuse as they pause rendering for the component and its children. Refer to the concurrent guide for information on how to best use async components.
diff --git a/docs/src/platforms/05-liveview.md b/docs/src/platforms/05-liveview.md
deleted file mode 100644
index cedd9b668..000000000
--- a/docs/src/platforms/05-liveview.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## Liveview
-
-With the Context, Subscription, and Asynchronous APIs, we've built Dioxus Liveview: a coupling of frontend and backend to deliver user experiences that do not require dedicated API development. Instead of building and maintaining frontend-specific API endpoints, components can directly access databases, server caches, and other services directly from the component.
-
-These set of features are still experimental. Currently, we're still working on making these components more ergonomic
-
-```rust
-fn live_component(cx: &Context<()>) -> DomTree {
- use_live_component(
- cx,
- // Rendered via the client
- #[cfg(target_arch = "wasm32")]
- || html! {
{"Loading data from server..."}
},
-
- // Rendered on the server
- #[cfg(not(target_arch = "wasm32"))]
- || html! {
{"Server Data Loaded!"}
},
- )
-}
-```
diff --git a/docs/src/platforms/06-components.md b/docs/src/platforms/06-components.md
deleted file mode 100644
index b6b02c0fb..000000000
--- a/docs/src/platforms/06-components.md
+++ /dev/null
@@ -1,31 +0,0 @@
-## Components
-
-Dioxus should look and feel just like writing functional React components. In Dioxus, there are no class components with lifecycles. All state management is done via hooks. This encourages logic reusability and lessens the burden on Dioxus to maintain a non-breaking lifecycle API.
-
-```rust
-#[derive(Properties, PartialEq)]
-struct MyProps {
- name: String
-}
-
-fn Example(cx: Context) -> DomTree {
- html! {
"Hello {cx.cx.name}!"
}
-}
-```
-
-Here, the `Context` object is used to access hook state, create subscriptions, and interact with the built-in context API. Props, children, and component APIs are accessible via the `Context` object. The functional component macro makes life more productive by inlining props directly as function arguments, similar to how Rocket parses URIs.
-
-```rust
-// A very terse component!
-#[fc]
-fn Example(cx: Context, name: String) -> DomTree {
- html! {
"Hello {name}!"
}
-}
-
-// or
-
-#[functional_component]
-pub static Example: FC = |cx, name: String| html! {
"Hello {name}!"
};
-```
-
-The final output of components must be a tree of VNodes. We provide an html macro for using JSX-style syntax to write these, though, you could use any macro, DSL, templating engine, or the constructors directly.
diff --git a/docs/src/setup.md b/docs/src/setup.md
index 861d5a73f..c8b2ba756 100644
--- a/docs/src/setup.md
+++ b/docs/src/setup.md
@@ -2,7 +2,7 @@
Dioxus aims to provide a fast, friendly, and portable toolkit for building user interfaces with Rust.
-This guide assumes you'll be building a SPA (single page application) for the web. The process for building desktop apps, server-rendered apps, static sites, and mobile apps, is more-or-less the same. You can check out the [Platform Specific Guides](../platforms/00-index.md) for more information on setting up Dioxus for any of the various targets you are building for.
+This Getting Setup guide assumes you'll be building a small desktop application. You can check out the [Platform Specific Guides](../platforms/00-index.md) for more information on setting up Dioxus for any of the various supported platforms.
# Setting up Dioxus
@@ -10,11 +10,13 @@ Dioxus requires a few main things to get up and running:
- The [Rust compiler](https://www.rust-lang.org) and associated build tooling
-- An editor of your choice, with the [Rust-Analyzer LSP plugin](https://rust-analyzer.github.io)
+- An editor of your choice, ideally configured with the [Rust-Analyzer LSP plugin](https://rust-analyzer.github.io)
Dioxus integrates very well with the Rust-Analyzer IDE plugin which will provide appropriate syntax highlighting, code navigation, folding, and more.
-We also recommend installing the Dioxus CLI. The Dioxus CLI automates building and packaging for various targets and integrates with simulators, development servers, and app deployment. It'll be our one-stop-shop for anything related to building and sharing our Dioxus Apps. To install the CLI, you'll need cargo (should be automatically installed with Rust):
+### Dioxus-CLI for dev server, bundling, etc.
+
+We also recommend installing the Dioxus CLI. The Dioxus CLI automates building and packaging for various targets and integrates with simulators, development servers, and app deployment. To install the CLI, you'll need cargo (should be automatically installed with Rust):
```
$ cargo install dioxus-cli
@@ -28,15 +30,17 @@ $ cargo install --force dioxus-cli
If your version of the CLI is out of date, it'll remind you to update whenever a new version is uploaded to Rust's package manager [crates.io](https://crates.io). We use a dedicated 1st-party CLI to save you from having to run potentially untrusted code every time you add a crate to your project - as is standard in the NPM ecosystem. You can vet the source of the Dioxus-CLI yourself at its [GitHub repo](https://github.com/jkelleyrtp/dioxus/tree/master/packages/cli).
+### Suggested extensions
+
If you want to keep your traditional `npm install XXX` workflow for adding packages, you might want to install `cargo-edit` and a few other fun `cargo` extensions:
- [cargo edit](https://github.com/killercup/cargo-edit) for adding dependencies from the CLI
- [cargo-expand](https://github.com/dtolnay/cargo-expand) for expanding macro calls
-- `cargo tree` - an integrated cargo command that lets you inspect your dependency tree
+- [cargo tree](https://doc.rust-lang.org/cargo/commands/cargo-tree.html) - an integrated cargo command that lets you inspect your dependency tree
-That's it! We won't need to touch NPM/WebPack/Babel/Parcel, etc.
+That's it! We won't need to touch NPM/WebPack/Babel/Parcel, etc. However, you _can_ configure your app to use WebPack with [traditional WASM-pack tooling](https://rustwasm.github.io/wasm-pack/book/tutorials/hybrid-applications-with-webpack/using-your-library.html).
-# Important tools
+## Rust Knowledge
With Rust, things like benchmarking, testing, and documentation are included in the language. We strongly recommend going through the official Rust book _completely_. However, our hope is that a Dioxus app can serve as a great first project. With Dioxus you'll learn about:
@@ -45,7 +49,7 @@ With Rust, things like benchmarking, testing, and documentation are included in
- Closures
- Macros
-We've put a lot of care into making Dioxus syntax familiar and easy to understand, so you won't need knowledge on async, lifetimes, and smart pointers until you really start building complex Dioxus apps.
+We've put a lot of care into making Dioxus syntax familiar and easy to understand, so you won't need deep knowledge on async, lifetimes, or smart pointers until you really start building complex Dioxus apps.
We strongly encourage exploring the guides for more information on how to work with the integrated tooling:
diff --git a/docs/src/tutorial/advanced_guides.md b/docs/src/tutorial/advanced_guides.md
new file mode 100644
index 000000000..53d2cb54f
--- /dev/null
+++ b/docs/src/tutorial/advanced_guides.md
@@ -0,0 +1 @@
+# Advanced Guides
diff --git a/docs/src/tutorial/components.md b/docs/src/tutorial/components.md
new file mode 100644
index 000000000..bfbb760b3
--- /dev/null
+++ b/docs/src/tutorial/components.md
@@ -0,0 +1 @@
+# Defining Components
diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md
new file mode 100644
index 000000000..48b7e493c
--- /dev/null
+++ b/docs/src/tutorial/index.md
@@ -0,0 +1,13 @@
+# Putting it all together
+
+So far, we've covered the basics of Dioxus. We've talked about:
+
+- Elements
+- Components
+- Interactivity
+- State Management
+- Async Actions
+- Styling
+
+In this chapter, we'll build a real-world weather app that combines everything we've learned into a cute application that you can run locally. It'll let us monitor different locations simultaneously and periodically check for updates.
+
diff --git a/docs/src/tutorial/new_app.md b/docs/src/tutorial/new_app.md
new file mode 100644
index 000000000..6c644c0bf
--- /dev/null
+++ b/docs/src/tutorial/new_app.md
@@ -0,0 +1 @@
+# New app
diff --git a/docs/src/tutorial/publishing.md b/docs/src/tutorial/publishing.md
new file mode 100644
index 000000000..96670f1c8
--- /dev/null
+++ b/docs/src/tutorial/publishing.md
@@ -0,0 +1 @@
+# Publishing
diff --git a/docs/src/tutorial/state.md b/docs/src/tutorial/state.md
new file mode 100644
index 000000000..35df690d6
--- /dev/null
+++ b/docs/src/tutorial/state.md
@@ -0,0 +1 @@
+# Defining State
diff --git a/docs/src/tutorial/structure.md b/docs/src/tutorial/structure.md
new file mode 100644
index 000000000..1cbb47579
--- /dev/null
+++ b/docs/src/tutorial/structure.md
@@ -0,0 +1 @@
+# Structuring our app
diff --git a/docs/src/tutorial/styling.md b/docs/src/tutorial/styling.md
new file mode 100644
index 000000000..3e90761b5
--- /dev/null
+++ b/docs/src/tutorial/styling.md
@@ -0,0 +1 @@
+# Styling
diff --git a/examples/hello_world.rs b/examples/hello_world.rs
new file mode 100644
index 000000000..db9e22899
--- /dev/null
+++ b/examples/hello_world.rs
@@ -0,0 +1,11 @@
+use dioxus::prelude::*;
+
+fn main() {
+ dioxus::desktop::launch(App, |c| c);
+}
+
+fn App((cx, props): Component<()>) -> DomTree {
+ cx.render(rsx! (
+ div { "Hello, world!" }
+ ))
+}
diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs
index d01a27f35..a132148c8 100644
--- a/packages/core-macro/src/lib.rs
+++ b/packages/core-macro/src/lib.rs
@@ -1,6 +1,5 @@
use proc_macro::TokenStream;
use quote::ToTokens;
-use rsx::{AS_HTML, AS_RSX};
use syn::parse_macro_input;
pub(crate) mod htm;
@@ -182,158 +181,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
/// ```
#[proc_macro]
pub fn rsx(s: TokenStream) -> TokenStream {
- match syn::parse::>(s) {
- Err(e) => e.to_compile_error().into(),
- Ok(s) => s.to_token_stream().into(),
- }
-}
-
-/// The html! macro makes it easy for developers to write jsx-style markup in their components.
-///
-/// ## Complete Reference Guide:
-/// ```
-/// const Example: FC<()> = |(cx, props)|{
-/// let formatting = "formatting!";
-/// let formatting_tuple = ("a", "b");
-/// let lazy_fmt = format_args!("lazily formatted text");
-/// cx.render(html! {
-///
-///
-///
-///
"Some text"
-///
"Some text with {formatting}"
-///
"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"
-///
-/// "Multiple"
-/// "Text"
-/// "Blocks"
-/// "Use comments as separators in html"
-///
))}
-/// {{
-/// let data = std::collections::HashMap::<&'static str, &'static str>::new();
-/// // Iterators *should* have keys when you can provide them.
-/// // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
-/// // Using an "ID" associated with your data is a good idea.
-/// data.into_iter().map(|(k, v)| rsx!(
"{v}"
))
-/// }}
-
-/// // Matching
-/// // Matching will throw a Rust error about "no two closures are the same type"
-/// // To fix this, call "render" method or use the "in" syntax to produce VNodes.
-/// // There's nothing we can do about it, sorry :/ (unless you want *really* unhygenic macros)
-/// {match true {
-/// true => rsx!(cx,
"Top text"
),
-/// false => rsx!(cx,
"Bottom text"
),
-/// }}
-///
-/// // Conditional rendering
-/// // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
-/// // You can convert a bool condition to rsx! with .then and .or
-/// {true.then(|| html!())}
-///
-/// // True conditions need to be rendered (same reasons as matching)
-/// {if true {
-/// html!(cx,
"Top text"
)
-/// } else {
-/// html!(cx,
"Bottom text"
)
-/// }}
-///
-/// // returning "None" is a bit noisy... but rare in practice
-/// {None as Option<()>}
-///
-/// // Use the Dioxus type-alias for less noise
-/// {NONE_ELEMENT}
-///
-/// // can also just use empty fragments
-///
-///
-/// // Fragments let you insert groups of nodes without a parent.
-/// // This lets you make components that insert elements as siblings without a container.
-///
"A"
-///
-///
"B"
-///
"C"
-///
-/// "D"
-///
-/// "heavily nested fragments is an antipattern"
-/// "they cause Dioxus to do unnecessary work"
-/// "don't use them carelessly if you can help it"
-///
-///
-///
-/// // Components
-/// // Can accept any paths
-/// // Notice how you still get syntax highlighting and IDE support :)
-///
-///
-///
-///
-/// // Can take properties
-///
-///
-/// // Can take optional properties
-///
-///
-/// // Can pass in props directly as an expression
-/// {{
-/// let props = TallerProps {a: "hello"};
-/// html!()
-/// }}
-///
-/// // Spreading can also be overridden manually
-///
-///
-/// // Can take children too!
-///
-///
"hello world!"
-///
-/// }
-/// })
-/// };
-///
-/// mod baller {
-/// use super::*;
-/// pub struct BallerProps {}
-///
-/// /// This component totally balls
-/// pub fn Baller(cx: Context<()>) -> DomTree {
-/// todo!()
-/// }
-/// }
-///
-/// #[derive(Debug, PartialEq, Props)]
-/// pub struct TallerProps {
-/// a: &'static str,
-/// }
-///
-/// /// This component is taller than most :)
-/// pub fn Taller(cx: Context) -> DomTree {
-/// let b = true;
-/// todo!()
-/// }
-/// ```
-#[proc_macro]
-pub fn html(s: TokenStream) -> TokenStream {
- match syn::parse::>(s) {
+ match syn::parse::(s) {
Err(e) => e.to_compile_error().into(),
Ok(s) => s.to_token_stream().into(),
}
diff --git a/packages/core-macro/src/props/mod.rs b/packages/core-macro/src/props/mod.rs
index cdfe7e8b6..c13f1b174 100644
--- a/packages/core-macro/src/props/mod.rs
+++ b/packages/core-macro/src/props/mod.rs
@@ -17,7 +17,7 @@ pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result {
let data = match &ast.data {
syn::Data::Struct(data) => match &data.fields {
syn::Fields::Named(fields) => {
- let struct_info = struct_info::StructInfo::new(&ast, fields.named.iter())?;
+ let struct_info = struct_info::StructInfo::new(ast, fields.named.iter())?;
let builder_creation = struct_info.builder_creation_impl()?;
let conversion_helper = struct_info.conversion_helper_impl()?;
let fields = struct_info
@@ -196,7 +196,7 @@ mod field_info {
if let Some(ref name) = field.ident {
Ok(FieldInfo {
ordinal,
- name: &name,
+ name,
generic_ident: syn::Ident::new(
&format!("__{}", strip_raw_ident_prefix(name.to_string())),
proc_macro2::Span::call_site(),
@@ -585,7 +585,7 @@ mod struct_info {
// we use the heuristic: are there *any* generic parameters?
// If so, then they might have non-static lifetimes and we can't compare two generic things that *might borrow*
// Therefore, we will generate code that shortcircuits the "comparison" in memoization
- let are_there_generics = self.generics.params.len() > 0;
+ let are_there_generics = !self.generics.params.is_empty();
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let all_fields_param = syn::GenericParam::Type(
diff --git a/packages/core-macro/src/rsx/ambiguous.rs b/packages/core-macro/src/rsx/ambiguous.rs
index f1ed461e8..71b3a2c4c 100644
--- a/packages/core-macro/src/rsx/ambiguous.rs
+++ b/packages/core-macro/src/rsx/ambiguous.rs
@@ -16,26 +16,22 @@ use syn::{
};
#[allow(clippy::large_enum_variant)]
-pub enum AmbiguousElement {
- Element(Element),
- Component(Component),
+pub enum AmbiguousElement {
+ Element(Element),
+ Component(Component),
}
-impl Parse for AmbiguousElement {
+impl Parse for AmbiguousElement {
fn parse(input: ParseStream) -> Result {
// Try to parse as an absolute path and immediately defer to the componetn
if input.peek(Token![::]) {
- return input
- .parse::>()
- .map(AmbiguousElement::Component);
+ return input.parse::().map(AmbiguousElement::Component);
}
// If not an absolute path, then parse the ident and check if it's a valid tag
if let Ok(pat) = input.fork().parse::() {
if pat.segments.len() > 1 {
- return input
- .parse::>()
- .map(AmbiguousElement::Component);
+ return input.parse::().map(AmbiguousElement::Component);
}
}
@@ -45,45 +41,16 @@ impl Parse for AmbiguousElement {
let first_char = name_str.chars().next().unwrap();
if first_char.is_ascii_uppercase() {
- input
- .parse::>()
- .map(AmbiguousElement::Component)
+ input.parse::().map(AmbiguousElement::Component)
} else {
- input
- .parse::>()
- .map(AmbiguousElement::Element)
+ input.parse::().map(AmbiguousElement::Element)
}
} else {
Err(Error::new(input.span(), "Not a valid Html tag"))
}
}
}
-
-impl Parse for AmbiguousElement {
- fn parse(input: ParseStream) -> Result {
- if input.peek(Token![<]) {
- let forked = input.fork();
- forked.parse::().unwrap();
- let tag = forked.parse::()?;
- let name_str = tag.to_string();
-
- let first_char = name_str.chars().next().unwrap();
- if first_char.is_ascii_uppercase() {
- input
- .parse::>()
- .map(AmbiguousElement::Component)
- } else {
- input
- .parse::>()
- .map(AmbiguousElement::Element)
- }
- } else {
- Err(Error::new(input.span(), "Not a valid Html tag"))
- }
- }
-}
-
-impl ToTokens for AmbiguousElement {
+impl ToTokens for AmbiguousElement {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
AmbiguousElement::Element(el) => el.to_tokens(tokens),
diff --git a/packages/core-macro/src/rsx/body.rs b/packages/core-macro/src/rsx/body.rs
index 375e0344a..b5b487459 100644
--- a/packages/core-macro/src/rsx/body.rs
+++ b/packages/core-macro/src/rsx/body.rs
@@ -7,30 +7,16 @@ use syn::{
use super::*;
-pub struct CallBody {
+pub struct CallBody {
custom_context: Option,
- roots: Vec>,
+ roots: Vec,
}
/// The custom rusty variant of parsing rsx!
-impl Parse for CallBody {
+impl Parse for CallBody {
fn parse(input: ParseStream) -> Result {
let custom_context = try_parse_custom_context(input)?;
- let (_, roots, _) = BodyConfig::::new_call_body().parse_component_body(input)?;
- Ok(Self {
- custom_context,
- roots,
- })
- }
-}
-
-/// The HTML variant of parsing rsx!
-impl Parse for CallBody {
- fn parse(input: ParseStream) -> Result {
- let custom_context = try_parse_custom_context(input)?;
-
- // parsing the contents is almost like parsing the inner of any element, but with no props
- let (_, roots, _) = BodyConfig::::new_call_body().parse_component_body(input)?;
+ let (_, roots, _) = BodyConfig::new_call_body().parse_component_body(input)?;
Ok(Self {
custom_context,
roots,
@@ -50,7 +36,7 @@ fn try_parse_custom_context(input: ParseStream) -> Result