Documentation
¶
Overview ¶
Package components provides server-side custom elements for Buffalo applications. It implements a component system similar to Web Components but rendered server-side, allowing you to create reusable UI components with a custom tag syntax.
Components are defined as <bk-*> tags in your HTML templates and are expanded server-side before sending to the client. This provides the benefits of components (reusability, encapsulation, composition) without requiring JavaScript.
Example usage in templates:
<bk-button variant="primary" href="/save">Save Changes</bk-button> <bk-card> <bk-slot name="header">Card Title</bk-slot> <p>Card content goes here</p> </bk-card>
The system supports:
- Custom attributes passed to components
- Named slots for content distribution
- Nested components
- Shadowing (apps can override built-in components)
Components are registered with the Registry and expanded by middleware that processes HTML responses before sending to clients.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExpanderMiddleware ¶
func ExpanderMiddleware(registry *Registry, devMode bool) buffalo.MiddlewareFunc
ExpanderMiddleware returns middleware that expands server-side components. This middleware intercepts HTML responses and processes any <bk-*> tags, replacing them with their rendered HTML before sending to the client.
How it works:
- Wraps the response writer to capture the HTML output
- Lets the handler generate its response
- If response is HTML, parses it and expands components
- Writes the expanded HTML to the real response writer
The middleware only processes text/html responses to avoid breaking JSON APIs, file downloads, etc.
When devMode is true, component boundary comments are added to help with debugging (e.g., <!-- bk-button --> ... <!-- /bk-button -->).
Usage:
app.Use(components.ExpanderMiddleware(registry, devMode))
WHY middleware: This approach allows components to work transparently with any template engine or HTML generation method. Templates don't need to know about component expansion - they just write <bk-*> tags.
Types ¶
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages server-side components. It's the central repository for all registered components in the application. Components are registered by name (e.g., "bk-button") with their renderer function.
The registry is used by the expansion middleware to look up and render components when processing HTML responses.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates a new component registry. This is typically called once during app initialization:
registry := components.NewRegistry() registry.RegisterDefaults() app.Use(components.ExpanderMiddleware(registry))
func (*Registry) Register ¶
Register adds a component to the registry. The name should follow the pattern "bk-*" to clearly identify it as a Buffkit component.
Example:
registry.Register("bk-avatar", func(attrs, slots map[string]string) ([]byte, error) { user := attrs["user"] size := attrs["size"] return []byte(fmt.Sprintf(`<img class="avatar-%s" src="/avatars/%s.jpg">`, size, user)), nil })
Components can be overridden by registering a new renderer with the same name. This allows apps to customize built-in components.
func (*Registry) RegisterDefaults ¶
func (r *Registry) RegisterDefaults()
RegisterDefaults is deprecated and does nothing. Apps should register their own components using Register().
Example:
registry.Register("my-button", func(attrs map[string]string, slots map[string]string) ([]byte, error) { // Your component rendering logic here return []byte("<button>" + slots["default"] + "</button>"), nil })
func (*Registry) Render ¶
func (r *Registry) Render(name string, attrs map[string]string, slots map[string]string) ([]byte, error)
Render renders a component by name. This looks up the component's renderer and calls it with the provided attributes and slots.
If the component doesn't exist, an error is returned and the original tag is preserved in the HTML (graceful degradation).
This method is called by the expansion middleware when it encounters a <bk-*> tag in the HTML.
type Renderer ¶
Renderer is a function that renders a component. It receives attributes from the component tag and any slot content, then returns the expanded HTML.
Example renderer:
func renderButton(attrs map[string]string, slots map[string]string) ([]byte, error) { variant := attrs["variant"] // Get variant attribute content := slots["default"] // Get default slot content return []byte(fmt.Sprintf(`<button class="btn-%s">%s</button>`, variant, content)), nil }
WHY: This signature allows components to be pure functions that transform attributes and content into HTML, making them easy to test and reason about.