Crates.io | frender |
lib.rs | frender |
version | 1.0.0-alpha.8 |
source | src |
created_at | 2022-01-28 11:35:57.218795 |
updated_at | 2022-03-02 15:57:21.821328 |
description | react in rust. functional rendering web ui |
homepage | |
repository | |
max_upload_size | |
id | 523046 |
size | 34,237 |
Functional Rendering: React
in Rust
frender is still in alpha and it's api might change.
For now it is recommended to specify the exact version in Cargo.toml
.
Before updating, please see the full changelog in case there are breaking changes.
There are some example apps in
examples
folder. You can preview them at this site.
Create a new cargo project
cargo new my-frender-app
cd my-frender-app
Add frender
to dependencies in Cargo.toml
.
[dependencies]
frender = "= 1.0.0-alpha.8"
Create index.html
in the project root directory.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>My frender App</title>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<link data-trunk rel="rust" href="Cargo.toml" />
</head>
<body>
<div id="frender-root"></div>
</body>
</html>
Modify src/main.rs
use frender::prelude::*;
#[component(main(mount_element_id = "frender-root"))]
fn Main() {
rsx!(
<div>
"Hello, frender!"
</div>
)
}
Run with trunk
Install trunk and then execute:
trunk serve
Then you can navigate to http://localhost:8080
to see your frender app.
rsx
syntaxrsx
elementuse frender::prelude::*;
rsx! (
<MyComp id="my-component">
// Child node can be any literal strings or numbers
"some string"
1
// Child node can be any rust expressions wrapped in braces
{ 1 + 6 }
{ value }
// Child node can be an element
<MyChild key="k" prop={any_expr} />
// Prop without value means `true`, just like React
<MyDialog show />
// Fragment
<>1 2 3</>
// Fragment with key
<# key="key">1 2 3</#>
// you can also use `</_>` to enclose any element
<path::to::Component></_>
// the above is equivalent to:
<path::to::Component></path::to::Component>
</MyComp>
)
Any component name starting with lower case letter [a-z]
will be interpreted as an intrinsic component.
For example, rsx!( <div id="my-div" /> )
will be resolved to:
use frender::prelude::*;
use self::intrinsic_components::div::prelude::*;
rsx! (
<self::intrinsic_components::div::prelude::Component id="my-div" />
)
rsx
propIn order to make rsx less verbose, frender provides
IntoPropValue
trait. The value
in
<MyComponent prop={value} />
will be mapped to
IntoPropValue::into_prop_value(value)
.
With this, assuming the prop accepts Option<i32>
,
you can simplify prop={Some(1)}
to prop={1}
,
because T
implements IntoPropValue<Option<T>>
.
If you want to pass the value as is, you can
use :=
to set prop. prop:={value}
use frender::prelude::*;
#[component]
fn MyComponent() {
// ^
// the return type defaults to react::Element
rsx!( <div /> )
}
// Or you can specify the return type explicitly
#[component]
fn MyAnotherComponent() -> Option<react::Element> {
if check_something() {
Some(rsx!( <MyComponent /> ))
} else {
None
}
}
First, define MyProps
use frender::prelude::*;
def_props! {
pub struct MyProps {
// Required prop
name: String,
// Optional prop which defaults to `Default::default()`
// The following property `age` is optional, and defaults to `None`
age?: Option<u8>,
// The following property `tags` is optional, and defaults to `Vec::default()`
tags?: Vec<String>,
// If the prop type is not specified,
// then frender will infer the type by prop name.
// For example, `class_name` default has type `Option<String>`
// The following property `class_name` is optional, has type Option<String>
class_name?,
// The following property `id` is required, has type Option<String>
id,
// Prop can also have type generics.
// For example, the following is
// the default definition for prop `children`,
// which means it accepts any `Option<TNode>` where TNode implements react::Node,
// and then map the value into `Option<react::Children>` and store it into MyProps.
children<TNode: react::Node>(value: Option<TNode>) -> Option<react::Children> {
value.and_then(react::Node::into_children)
},
}
}
Then write the component with the above props:
use frender::prelude::*;
#[component]
pub fn MyComponent(props: &MyProps) {
rsx!(<div>{&props.children}</div>)
}
Due to the generics, in some very rare cases, you may meet errors like
type annotations needed
cannot infer type for type parameter
.
You can solve it by specifying the type
with the turbofish syntax ::<>
.
For example:
rsx! (
// ERROR: type annotations needed
<a children={None} />
)
rsx! (
// it works!
<a children={None::<()>} />
)
React hooks are also available in frender
.
You checkout the examples for the usage.
frender
components to jsCssProperties
emotion/react
)frender
is open sourced at GitHub.
Pull requests and issues are welcomed.
You can also sponsor me and I would be very grateful :heart: