GPUI-RSX
English | 简体中文
A Rust procedural macro that provides JSX-like syntax for GPUI, making UI development more concise and intuitive.
✨ Features
- 🎨 HTML-like Syntax - React JSX-like development experience
- 🚀 Zero Runtime Overhead - Expands to native GPUI code at compile time
- 📦 Lightweight - Only depends on
syn,quote,proc-macro2 - 🔧 Flexible - Supports expressions, conditional rendering, component composition
- 💡 Type Safe - Full compile-time type checking
- 🧩 Fragment Support - Return multiple root elements with
<>...</> - 🔁 For-loop Sugar - Iterate with
{for item in iter { ... }} - 🎨 Full Tailwind Colors - 242 built-in colors + arbitrary hex values
📚 Documentation
- Architecture Guide - Detailed architecture documentation
- Module organization and data flow
- Code generation strategies
- Design patterns and testing approach
- Extension points and debugging guide
- Getting Started - Step-by-step tutorial
- API Reference - Complete API documentation
- Best Practices - Recommended patterns
- Migration Guide - Upgrade instructions
- Troubleshooting - Common issues and solutions
📦 Installation
Add to your Cargo.toml:
[]
= "0.1"
= "0.1"
🚀 Quick Start
Get Started in 5 Minutes
use *;
use rsx;
Before & After
❌ Traditional GPUI (Verbose)
✅ With GPUI-RSX (Concise)
See the Quick Start example above.
Code Reduction: ~50% ✨
📖 Syntax Guide
1. Basic Elements
rsx!
Expands to:
div.child
2. Fragment (Multiple Root Elements)
When you need to return multiple elements without a wrapper:
rsx!
Expands to:
vec!
3. Attributes
Boolean Attributes (Flags)
rsx!
Expands to:
div.flex.flex_col
Value Attributes
rsx!
Expands to:
div.gap.bg
4. Class Attribute
The class attribute accepts a Tailwind-like string that expands into multiple GPUI method calls:
rsx!
Expands to:
div.flex.flex_col.gap.p
Note:
classonly accepts string literals. For dynamic styling, use individual attributes (e.g.,bg={color_var}).
Supported class patterns
Layout:
flex,flex-col,flex-row,flex-wrap,flex-1,flex-none,flex-autoitems-center,items-start,items-endjustify-center,justify-between
Spacing (numeric values become px(n)):
gap-4→.gap(px(4.0))p-4,px-4,py-4,pt-4,pb-4,pl-4,pr-4m-4,mx-4,my-4,mt-4,mb-4,ml-4,mr-4w-64,h-32- Fractional values:
p-0.5→.p(px(0.5))
Sizing:
w-full,h-full,size-full
Text:
text-xs,text-sm,text-base,text-lg,text-xltext-2xl,text-3xl,text-4xl,text-5xlfont-bold
Border:
border→.border_1()border-2→.border_2(),border-4→.border_4()rounded-sm,rounded-md,rounded-lg,rounded-xl,rounded-full,rounded-none
Colors (full Tailwind palette):
text-red-500→.text_color(rgb(0xef4444))bg-blue-600→.bg(rgb(0x2563eb))border-green-500→.border_color(rgb(0x22c55e))- Arbitrary hex:
bg-[#ff0000],text-[#333],border-[#abc]
Effects:
shadow-sm,shadow-md,shadow-lgoverflow-hidden,overflow-scrollcursor-pointer,cursor-default,cursor-text
Supported colors: slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose (shades 50-950) + white, black
5. Event Handling
rsx!
Supported events (camelCase / snake_case):
| Event | Method |
|---|---|
onClick / on_click |
.on_click(handler) |
onMouseDown / on_mouse_down |
.on_mouse_down(handler) |
onMouseUp / on_mouse_up |
.on_mouse_up(handler) |
onMouseMove / on_mouse_move |
.on_mouse_move(handler) |
onMouseDownOut / on_mouse_down_out |
.on_mouse_down_out(handler) |
onMouseUpOut / on_mouse_up_out |
.on_mouse_up_out(handler) |
onKeyDown / on_key_down |
.on_key_down(handler) |
onKeyUp / on_key_up |
.on_key_up(handler) |
onFocus / on_focus |
.on_focus(handler) |
onBlur / on_blur |
.on_blur(handler) |
onHover / on_hover |
.on_hover(handler) |
onScrollWheel / on_scroll_wheel |
.on_scroll_wheel(handler) |
onDrag / on_drag |
.on_drag(handler) |
onDrop / on_drop |
.on_drop(handler) |
onAction / on_action |
.on_action(handler) |
6. Nested Elements
rsx!
7. Expressions
rsx!
8. List Rendering
Using iterators (traditional)
rsx!
Using for-loop syntax sugar
rsx!
Expands to:
div.children
For-loops also support ranges and method calls:
rsx!
9. Spread Syntax
rsx!
10. Attribute Mapping Reference
camelCase attributes are automatically mapped to GPUI snake_case methods:
| RSX Attribute | GPUI Method |
|---|---|
zIndex |
.z_index() |
opacity |
.opacity() |
visible |
.visible() |
invisible (flag) |
.visible(false) |
width / height |
.w() / .h() |
minWidth / maxWidth |
.min_w() / .max_w() |
minHeight / maxHeight |
.min_h() / .max_h() |
gapX / gapY |
.gap_x() / .gap_y() |
flexBasis |
.basis() |
flexGrow / flexShrink |
.flex_grow() / .flex_shrink() |
flexOrder |
.order() |
fontSize |
.font_size() |
lineHeight |
.line_height() |
fontWeight |
.font_weight() |
textAlign |
.text_align() |
textDecoration |
.text_decoration() |
borderRadius |
.border_radius() |
borderTop / borderBottom |
.border_t() / .border_b() |
borderLeft / borderRight |
.border_l() / .border_r() |
roundedTop / roundedBottom |
.rounded_t() / .rounded_b() |
roundedTopLeft / roundedTopRight |
.rounded_tl() / .rounded_tr() |
roundedBottomLeft / roundedBottomRight |
.rounded_bl() / .rounded_br() |
boxShadow |
.shadow() |
overflowX / overflowY |
.overflow_x_hidden() / .overflow_y_hidden() |
inset |
.inset() |
Attributes not in this table are passed through as-is (e.g., bg={color} → .bg(color)).
11. Conditional Styling with when and whenSome
when - Apply styles based on condition
rsx!
whenSome - Apply styles when Option has value
let custom_width: = Some;
rsx!
Multiple conditions
rsx!
12. Styled Flag (Default Tag Styles)
The styled flag injects sensible default styles based on the tag name:
rsx!
Default styles per tag:
| Tag | Default Styles |
|---|---|
h1 |
text-3xl font-bold |
h2 |
text-2xl font-bold |
h3 |
text-xl font-bold |
h4 |
text-lg font-bold |
h5 |
text-base font-bold |
h6 |
text-sm font-bold |
button, a |
cursor-pointer |
input, textarea |
px-2 py-1 |
ul, ol |
flex flex-col |
User attributes are applied after defaults and can override them.
🎯 Complete Example
Todo App
use *;
use rsx;
🔧 Advanced Usage
Custom Components
Conditional Rendering
rsx!
Dynamic Styling
📊 Performance
GPUI-RSX is a compile-time macro that expands to the same code as hand-written GPUI, with zero runtime overhead.
| Metric | Traditional GPUI | GPUI-RSX |
|---|---|---|
| Code Size | 100 lines | 50 lines (-50%) |
| Runtime Performance | Baseline | Same |
| Type Safety | ✅ | ✅ |
| Compile-time Checking | ✅ | ✅ |
🛠️ Development
Build
Test
Expand Macros (Debugging)
# Install cargo-expand
# View expanded code
💡 Best Practices
1. Component Splitting
Break complex UIs into small, reusable components:
// ✅ Recommended: Split into multiple methods
2. Use Constants
Extract repeated styles as constants:
const PRIMARY_BG: Rgb = rgb;
const PRIMARY_TEXT: Rgb = rgb;
rsx!
3. Avoid Over-nesting
// ❌ Not recommended: Over-nested
rsx!
// ✅ Recommended: Flatten structure
rsx!
🐛 FAQ
Q1: How to use variables in RSX?
let title = "Hello";
rsx!
Q2: How to handle Option types?
rsx!
Q3: What does the expanded macro code look like?
Use cargo expand to view:
Q4: Which elements are supported?
All GPUI-supported elements can be used, such as div, button, input, span, etc.
Q5: Can I use dynamic class values?
No. The class attribute only accepts string literals. For dynamic styling, use individual attributes:
// ❌ Won't work
rsx!
// ✅ Use individual attributes
rsx!
// ✅ Or use `when` for conditional styles
rsx!
🤝 Contributing
Contributions are welcome! Feel free to submit Issues or Pull Requests.
Development Workflow
- Fork the project
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push branch:
git push origin feature/amazing-feature - Submit a Pull Request
Code Standards
- Use
rustfmtto format code - Use
clippyto check code quality - Add tests for new features
- Update documentation
📝 License
MIT License
🙏 Acknowledgments
Inspired by:
- Dioxus RSX - RSX syntax design
- Yew html! macro - html! macro
- React JSX - JSX syntax
- GPUI - Underlying UI framework
Make GPUI development more enjoyable! 🎉