# frender [![Crates.io](https://img.shields.io/crates/v/frender?style=for-the-badge)](https://crates.io/crates/frender) [![docs.rs](https://img.shields.io/docsrs/frender/latest?style=for-the-badge)](https://docs.rs/frender) [![GitHub license](https://img.shields.io/github/license/frender-rs/frender?style=for-the-badge)](https://github.com/frender-rs/frender/blob/main/LICENSE) [![GitHub stars](https://img.shields.io/github/stars/frender-rs/frender?style=for-the-badge)](https://github.com/frender-rs/frender/stargazers)
![frender logo](./logo.svg) Functional Rendering: `React` in `Rust`
_**f**render_ 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](https://github.com/frender-rs/frender/blob/alpha/CHANGELOG.md) in case there are breaking changes. There are some example apps in [`examples`](https://github.com/frender-rs/frender/tree/alpha/examples) folder. You can preview them at [this site](https://frender-rs.github.io/frender/). ## Quick Start 1. Create a new cargo project ```sh cargo new my-frender-app cd my-frender-app ``` 2. Add `frender` to dependencies in `Cargo.toml`. ```toml [dependencies] frender = "= 1.0.0-alpha.8" ``` 3. Create `index.html` in the project root directory. ```html My frender App
``` 4. Modify `src/main.rs` ```rust use frender::prelude::*; #[component(main(mount_element_id = "frender-root"))] fn Main() { rsx!(
"Hello, frender!"
) } ``` 5. Run with `trunk` Install [trunk](https://trunkrs.dev/#install) and then execute: ```sh trunk serve ``` Then you can navigate to `http://localhost:8080` to see your frender app. ## `rsx` syntax ### `rsx` element ```rust use frender::prelude::*; rsx! ( // 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 // Prop without value means `true`, just like React // Fragment <>1 2 3 // Fragment with key <# key="key">1 2 3 // you can also use `` to enclose any element // the above is equivalent to: ) ``` Any component name starting with lower case letter `[a-z]` will be interpreted as an **intrinsic component**. For example, `rsx!(
)` will be resolved to: ```rust use frender::prelude::*; use self::intrinsic_components::div::prelude::*; rsx! ( ) ``` ### `rsx` prop In order to make rsx less verbose, frender provides `IntoPropValue` trait. The `value` in `` will be mapped to `IntoPropValue::into_prop_value(value)`. With this, assuming the prop accepts `Option`, you can simplify `prop={Some(1)}` to `prop={1}`, because `T` implements `IntoPropValue>`. If you want to pass the value as is, you can use `:=` to set prop. `prop:={value}` ## Write a component ### Component with no props ```rust use frender::prelude::*; #[component] fn MyComponent() { // ^ // the return type defaults to react::Element rsx!(
) } // Or you can specify the return type explicitly #[component] fn MyAnotherComponent() -> Option { if check_something() { Some(rsx!( )) } else { None } } ``` ### Component with props First, define `MyProps` ```rust 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, // The following property `tags` is optional, and defaults to `Vec::default()` tags?: Vec, // If the prop type is not specified, // then frender will infer the type by prop name. // For example, `class_name` default has type `Option` // The following property `class_name` is optional, has type Option class_name?, // The following property `id` is required, has type Option id, // Prop can also have type generics. // For example, the following is // the default definition for prop `children`, // which means it accepts any `Option` where TNode implements react::Node, // and then map the value into `Option` and store it into MyProps. children(value: Option) -> Option { value.and_then(react::Node::into_children) }, } } ``` Then write the component with the above props: ```rust use frender::prelude::*; #[component] pub fn MyComponent(props: &MyProps) { rsx!(
{&props.children}
) } ``` 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: ```rust rsx! ( // ERROR: type annotations needed ) rsx! ( // it works! } /> ) ``` ## Hooks React hooks are also available in `frender`. You checkout the [examples](https://github.com/frender-rs/frender/blob/alpha/examples/counter/src/my_counter.rs) for the usage. ## Future Development Plans - [ ] Documentation - [ ] Intrinsic svg components - [ ] Export `frender` components to js - [ ] Server Side Rendering - [ ] Type checking for `CssProperties` - [ ] Css-in-rust (For example, integrate with [`emotion/react`](https://emotion.sh/docs/@emotion/react)) ## Contributing `frender` is open sourced at [GitHub](https://github.com/frender-rs/frender). Pull requests and issues are welcomed. You can also [sponsor me](https://ko-fi.com/equalma) and I would be very grateful :heart: [![Buy Me a Coffee at ko-fi.com](https://cdn.ko-fi.com/cdn/kofi2.png?v=3)](https://ko-fi.com/N4N26J11L)