new-component▌
longbridge/gpui-component · updated Apr 8, 2026
When creating new GPUI components:
Instructions
When creating new GPUI components:
- Follow existing patterns: Base implementation on components in
crates/ui/src(examples:Button,Select,Dialog) - Style consistency: Follow existing component styles and Shadcn UI patterns
- Component type decision:
- Use stateless elements for simple components (like
Button) - Use stateful elements for complex components with data (like
SelectandSelectState) - Use composition for components built on existing components (like
AlertDialogbased onDialog)
- Use stateless elements for simple components (like
- API consistency: Maintain the same API style as other elements
- Documentation: Create component documentation
- Stories: Write component stories in the story folder
- Registration: Add the component to
crates/story/src/main.rsstory list
Component Types
- Stateless: Pure presentation components without internal state (e.g.,
Button) - Stateful: Components that manage their own state and data (e.g.,
Select) - Composite: Components built on top of existing components (e.g.,
AlertDialogbased onDialog)
Implementation Steps
1. Create Component File
Create a new file in crates/ui/src/ (e.g., alert_dialog.rs):
use gpui::{App, ClickEvent, Pixels, SharedString, Window, px};
use std::rc::Rc;
pub struct AlertDialog {
pub(crate) variant: AlertVariant,
pub(crate) title: SharedString,
// ... other fields
}
impl AlertDialog {
pub fn new(title: impl Into<SharedString>) -> Self {
// implementation
}
// Builder methods
pub fn description(mut self, desc: impl Into<SharedString>) -> Self {
// implementation
}
}
2. Register in lib.rs
Add the module to crates/ui/src/lib.rs:
pub mod alert_dialog;
3. Extend WindowExt (if needed)
For dialog-like components, add helper methods to window_ext.rs:
pub trait WindowExt {
fn open_alert_dialog(&mut self, alert: AlertDialog, cx: &mut App);
}
4. Create Story
Create crates/story/src/stories/alert_dialog_story.rs:
pub struct AlertDialogStory {
focus_handle: FocusHandle,
}
impl Story for AlertDialogStory {
fn title() -> &'static str {
"AlertDialog"
}
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render> {
Self::view(window, cx)
}
}
5. Register Story
Add to crates/story/src/stories/mod.rs:
mod alert_dialog_story;
pub use alert_dialog_story::AlertDialogStory;
Add to crates/story/src/main.rs in the stories list:
vec![
StoryContainer::panel::<AlertStory>(window, cx),
StoryContainer::panel::<AlertDialogStory>(window, cx), // Add here
// ...
]
Real Example: AlertDialog
AlertDialog is a composite component based on Dialog with these features:
- Simpler API: Pre-configured for common alert scenarios
- Center-aligned layout: All content (icon, title, description, buttons) is center-aligned
- Vertical layout: Icon appears at the top, followed by title and description
- Auto icons: Automatically shows icons based on variant (Info, Success, Warning, Error)
- Convenience constructors:
AlertDialog::info(),AlertDialog::warning(), etc.
Key Design Decisions:
descriptionusesSharedStringinstead ofAnyElementbecause the Dialog builder needs to beFn(callable multiple times), andAnyElementcannot be cloned- Implementation is in
window_ext.rsusing Dialog as the base, not as a separate IntoElement component - Center-aligned layout: Icon is positioned at the top (not left), all text is center-aligned for a more focused alert appearance
- Footer center-aligned: Buttons are centered, different from Dialog's default right-aligned footer
Usage:
window.open_alert_dialog(
AlertDialog::warning("Unsaved Changes")
.description("You have unsaved changes.")
.show_cancel(true)
.on_confirm(|_, window, cx| {
window.push_notification("Confirmed", cx);
true
}),
cx,
);
Common Patterns
Builder Pattern
All components use the builder pattern for configuration:
AlertDialog::new("Title")
.description("Description")
.width(px(500.))
.on_confirm(|_, _, _| true)
Size Variants
Implement Sizable trait for components that support size variants (xs, sm, md, lg).
Variants
Use enums for visual variants (e.g., AlertVariant::Info, ButtonVariant::Primary).
Styled Trait Implementation
Components that render as a single container element should implement Styled to allow callers to customize styles. The pattern uses a StyleRefinement field and refine_style() from StyledExt:
use gpui::{AnyElement, App, IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, Window, div};
use crate::StyledExt as _;
#[derive(IntoElement)]
pub struct MyComponent {
style: StyleRefinement,
children: Vec<AnyElement>,
}
impl MyComponent {
pub fn new() -> Self {
Self {
style: StyleRefinement::default(),
children: Vec::new(),
}
}
}
impl ParentElement for MyComponent {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.children.extend(elements);
}
}
impl Styled for MyComponent {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl RenderOnce for MyComponent {
fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
div()
// ... component's default styles ...
.refine_style(&self.style) // Apply user's style overrides
.children(self.children)
}
}
Key points:
- Add
style: StyleRefinementfield initialized withStyleRefinement::default() - Implement
Styledtrait returning&mut self.style - In
render(), call.refine_style(&self.style)on the root div to merge user styles - Place
.refine_style()after component defaults but before.children()so user styles override defaults - Reference:
crates/ui/src/dialog/header.rs(DialogHeader),crates/ui/src/table/table.rs(Table and sub-components)
Callbacks
Use Rc<dyn Fn> for callbacks that may be called multiple times:
on_confirm: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static>>
Ratings
4.5★★★★★10 reviews- ★★★★★Shikha Mishra· Oct 10, 2024
new-component is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
- ★★★★★Piyush G· Sep 9, 2024
Keeps context tight: new-component is the kind of skill you can hand to a new teammate without a long onboarding doc.
- ★★★★★Chaitanya Patil· Aug 8, 2024
Registry listing for new-component matched our evaluation — installs cleanly and behaves as described in the markdown.
- ★★★★★Sakshi Patil· Jul 7, 2024
new-component reduced setup friction for our internal harness; good balance of opinion and flexibility.
- ★★★★★Ganesh Mohane· Jun 6, 2024
I recommend new-component for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
- ★★★★★Oshnikdeep· May 5, 2024
Useful defaults in new-component — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
- ★★★★★Dhruvi Jain· Apr 4, 2024
new-component has been reliable in day-to-day use. Documentation quality is above average for community skills.
- ★★★★★Rahul Santra· Mar 3, 2024
Solid pick for teams standardizing on skills: new-component is focused, and the summary matches what you get after install.
- ★★★★★Pratham Ware· Feb 2, 2024
We added new-component from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
- ★★★★★Yash Thakker· Jan 1, 2024
new-component fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.