A comprehensive Go wrapper library for Basecoat UI components, built on top of gomponents. Write beautiful, type-safe UI components in pure Go with no templating languages required.
โจ Pure Go - No templating languages, just Go functions
๐จ Component Library - Pre-built Basecoat UI components
๐ Dark Mode Support - Built-in dark mode with system preference detection and localStorage persistence
๐ Type-Safe - Compile-time checking for your UI components
โก Minimal JavaScript - Renders to pure HTML with minimal JavaScript
๐ฏ Framework Agnostic - Works with any Go web framework
go get maragu.dev/gomponents
go get github.com/wawandco/gomuiThen include Basecoat UI CSS and JavaScript using our helper components in your HTML:
<!-- Option 1: Include both CSS and JS (recommended) -->
<!-- In your Go code: gm.BasecoatAssets() -->
<!-- Option 2: Include CSS and JS separately -->
<!-- In your Go code: gm.BasecoatCSS() and gm.BasecoatJS() -->
<!-- Or manually include Basecoat assets -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/basecoat-css@0.3.10-beta.2/dist/basecoat.cdn.min.css">
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@0.3.10-beta.2/dist/js/all.min.js" defer></script>package main
import (
g "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
gm "github.com/wawandco/gomui"
)
func MyPage() g.Node {
return Html(
Head(
TitleEl(g.Text("My App")),
gm.DarkModeScript(), // Enable dark mode (place first)
gm.BasecoatAssets(), // Include Basecoat CSS and JS
),
Body(
Div(Class("container mx-auto p-4"),
gm.Card(
gm.CardHeader(
gm.CardTitle(g.Text("Welcome!")),
gm.CardDescription(g.Text("Get started with Basecoat UI")),
),
gm.CardContent(
gm.Button(gm.ButtonPrimary, gm.ButtonDefault,
g.Text("Click me"),
),
),
),
),
),
)
}Add the dark mode script in your <head> to initialize the theme:
Head(
gm.DarkModeScript(),
// ... other head elements
)Add a theme toggle button anywhere in your UI:
gm.ThemeToggle(
// Add your SVG icons for light/dark mode here
g.Raw(`<span class="hidden dark:block">โ๏ธ</span><span class="block dark:hidden">๐</span>`),
)The theme preference is automatically saved to localStorage and persists across sessions. The system also detects the user's OS preference on first load.
// Primary button
gm.Button(gm.ButtonPrimary, gm.ButtonDefault, g.Text("Primary"))
// Secondary button
gm.Button(gm.ButtonSecondary, gm.ButtonDefault, g.Text("Secondary"))
// Outline button
gm.Button(gm.ButtonOutline, gm.ButtonDefault, g.Text("Outline"))
// Ghost button
gm.Button(gm.ButtonGhost, gm.ButtonDefault, g.Text("Ghost"))
// Destructive button
gm.Button(gm.ButtonDestructive, gm.ButtonDefault, g.Text("Delete"))
// Different sizes
gm.Button(gm.ButtonPrimary, gm.ButtonSm, g.Text("Small"))
gm.Button(gm.ButtonPrimary, gm.ButtonLg, g.Text("Large"))
gm.Button(gm.ButtonPrimary, gm.ButtonIcon, g.Text("๐"))Use LinkButtonEl when you need navigation that appears as a button:
// Primary link button
gm.LinkButtonEl(gm.ButtonPrimary, gm.ButtonDefault, "/home", g.Text("Go Home"))
// Small secondary link button
gm.LinkButtonEl(gm.ButtonSecondary, gm.ButtonSm, "/settings", g.Text("Settings"))
// Large outline link button
gm.LinkButtonEl(gm.ButtonOutline, gm.ButtonLg, "/docs", g.Text("Documentation"))gm.Form(
gm.Field(
gm.Label(g.Attr("for", "email"), g.Text("Email")),
gm.Input(h.Type("email"), h.ID("email"), h.Placeholder("you@example.com")),
gm.FieldDescription(g.Text("We'll never share your email.")),
),
gm.Field(
gm.Label(g.Attr("for", "message"), g.Text("Message")),
gm.Textarea(h.ID("message"), h.Placeholder("Your message...")),
),
gm.Button(gm.ButtonPrimary, gm.ButtonDefault, g.Text("Submit")),
)gm.Card(
gm.CardHeader(
gm.CardTitle(g.Text("Card Title")),
gm.CardDescription(g.Text("Card description goes here")),
),
gm.CardContent(
g.Text("Your content here"),
),
gm.CardFooter(
gm.Button(gm.ButtonPrimary, gm.ButtonDefault, g.Text("Action")),
),
)// Default alert
gm.Alert(
gm.AlertTitle(g.Text("Heads up!")),
gm.AlertDescription(g.Text("This is an alert message.")),
)
// Destructive alert
gm.AlertDestructive(
gm.AlertTitle(g.Text("Error")),
gm.AlertDescription(g.Text("Something went wrong.")),
)gm.Badge(gm.BadgePrimary, g.Text("Primary"))
gm.Badge(gm.BadgeSecondary, g.Text("Secondary"))
gm.Badge(gm.BadgeDestructive, g.Text("Destructive"))
gm.Badge(gm.BadgeOutline, g.Text("Outline"))gm.Table(
gm.TableHeader(
gm.TableRow(
gm.TableHead(g.Text("Name")),
gm.TableHead(g.Text("Email")),
gm.TableHead(g.Text("Role")),
),
),
gm.TableBody(
gm.TableRow(
gm.TableCell(g.Text("John Doe")),
gm.TableCell(g.Text("john@example.com")),
gm.TableCell(g.Text("Admin")),
),
),
)gm.Dialog(
gm.DialogHeader(
gm.DialogTitle(g.Text("Confirm Action")),
gm.DialogDescription(g.Text("Are you sure?")),
),
gm.DialogSection(
// Dialog content here
),
gm.DialogFooter(
gm.Button(gm.ButtonOutline, gm.ButtonDefault, g.Text("Cancel")),
gm.Button(gm.ButtonPrimary, gm.ButtonDefault, g.Text("Confirm")),
),
)gm.Tabs(
gm.TabsList(
gm.TabItem("account-tab", true, g.Text("Account")),
gm.TabItem("password-tab", false, g.Text("Password")),
),
gm.TabPanel("account-panel", "account-tab", true,
gm.Card(
gm.CardHeader(
gm.CardTitle(g.Text("Account Settings")),
),
gm.CardContent(
g.Text("Account content here"),
),
),
),
gm.TabPanel("password-panel", "password-tab", false,
gm.Card(
gm.CardHeader(
gm.CardTitle(g.Text("Password Settings")),
),
gm.CardContent(
g.Text("Password content here"),
),
),
),
)gm.Accordion(
gm.AccordionItem(
gm.AccordionTrigger(g.Text("What is Basecoat UI?")),
gm.AccordionContent(
g.Text("Basecoat UI is a component library..."),
),
),
gm.AccordionItem(
gm.AccordionTrigger(g.Text("How do I use it?")),
gm.AccordionContent(
g.Text("Simply import the components..."),
),
),
)gm.DropdownMenu(
gm.DropdownMenuTrigger(g.Text("Options")),
gm.DropdownMenuContent(
gm.DropdownMenuMenu(
gm.DropdownMenuGroup(
gm.DropdownMenuGroupHeading(g.Text("My Account")),
gm.DropdownMenuItem(g.Text("Profile")),
gm.DropdownMenuItem(g.Text("Settings")),
),
gm.DropdownMenuSeparator(),
gm.DropdownMenuItem(g.Text("Logout")),
),
),
)All components are validated against Basecoat UI v0.3.10-beta.2 documentation.
Card,CardWithClasses,CardHeader,CardTitle,CardDescription,CardContent,CardFooter
Tabs,TabsList,TabItem,TabPanelDropdownMenu,DropdownMenuTrigger,DropdownMenuContent,DropdownMenuMenu,DropdownMenuItem,DropdownMenuItemCheckbox,DropdownMenuItemRadio,DropdownMenuGroup,DropdownMenuGroupHeading,DropdownMenuSeparator
Form,Field,FieldLabel,FieldDescription,FieldError,FieldsetInput,TextareaLabelCheckbox,Radio,SwitchSlider,SliderScript()Selectcomponents (SelectContainer, SelectButton, SelectPopover, SelectHeader, SelectListbox, SelectOptionEl, SelectHiddenInput, SelectWithSearch, Combobox)
Button,ButtonEl,ButtonWithClasses(with variants: Primary, Secondary, Outline, Ghost, Link, Destructive)LinkButton,LinkButtonEl,LinkButtonWithClasses- Anchor elements with button stylingButtonGroup,ButtonGroupWithOrientation,ButtonGroupSeparator
Table,TableEl,TableHeader,TableBody,TableFooter,TableRow,TableHead,TableData,TableCell,TableCaptionBadge(with variants: Primary, Secondary, Destructive, Outline)Kbd,KbdWithClasses,KbdEl
Alert,AlertDestructive,AlertTitle,AlertDescriptionToast,ToastContent,ToastSection,ToastFooter,ToastTitle,ToastDescription,ToastAction,Toaster
Dialog,DialogHeader,DialogTitle,DialogDescription,DialogSection,DialogFooterAlertDialog,AlertDialogHeader,AlertDialogTitle,AlertDialogDescription,AlertDialogSection,AlertDialogFooterSheet,SheetHeader,SheetTitle,SheetDescription,SheetSection,SheetFooterPopover,PopoverContent,PopoverHeaderHoverCard,HoverCardContent,HoverCardHeader
Command,CommandDialog,CommandHeader,CommandInput,CommandMenu,CommandGroup,CommandGroupHeading,CommandItem,CommandSeparator
Sidebar,SidebarHeader,SidebarNav,SidebarSection,SidebarGroup,SidebarHeading,SidebarList,SidebarItem,SidebarFooter,SidebarToggle,SidebarOpen,SidebarClose,SidebarAssets()
DarkModeScript()- Initialize dark mode with system preference detectionThemeToggle()- Theme toggle button (dispatchesbasecoat:themeevent)BasecoatCSS()- Include Basecoat CSS from CDN (v0.3.10-beta.2)BasecoatJS()- Include Basecoat JavaScript from CDN (v0.3.10-beta.2)BasecoatAssets()- Include both CSS and JavaScript
Components NOT included (not in Basecoat or use Tailwind utilities instead):
- Breadcrumb, Pagination - Use Tailwind utility classes with button components
- Avatar, Skeleton, Progress - Use Tailwind utilities
- Separator - Use
<hr role="separator">directly - Tooltip - Use
data-tooltipattribute on elements - NavigationMenu, ContextMenu, Menubar - Not in Basecoat
- ScrollArea, AspectRatio, Calendar, Carousel - Not in Basecoat
package main
import (
"net/http"
g "maragu.dev/gomponents"
gm "github.com/wawandco/gomui"
)
func handler(w http.ResponseWriter, r *http.Request) {
page := MyPage()
page.Render(w)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}package main
import (
"net/http"
"github.com/go-chi/chi/v5"
g "maragu.dev/gomponents"
gm "github.com/wawandco/gomui"
)
func main() {
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
MyPage().Render(w)
})
http.ListenAndServe(":8080", r)
}package main
import (
"github.com/gofiber/fiber/v2"
gm "github.com/wawandco/gomui"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html")
return MyPage().Render(c)
})
app.Listen(":8080")
}package main
import (
"github.com/labstack/echo/v4"
gm "github.com/wawandco/gomui"
)
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
c.Response().Header().Set("Content-Type", "text/html")
return MyPage().Render(c.Response())
})
e.Start(":8080")
}Create reusable component functions:
func UserCard(name, email string) g.Node {
return gm.Card(
gm.CardHeader(
gm.CardTitle(g.Text(name)),
gm.CardDescription(g.Text(email)),
),
gm.CardFooter(
gm.Button(gm.ButtonPrimary, gm.ButtonDefault, g.Text("View Profile")),
),
)
}func PageLayout(title string, content g.Node) g.Node {
return Html(
Head(
TitleEl(g.Text(title)),
gm.DarkModeScript(), // Must be first
gm.BasecoatAssets(),
),
Body(
Div(Class("container mx-auto p-4"),
gm.ThemeToggle(
g.Raw(`<span class="hidden dark:block">โ๏ธ</span><span class="block dark:hidden">๐</span>`),
),
content,
),
),
)
}import "maragu.dev/gomponents"
func ConditionalAlert(hasError bool, message string) g.Node {
return gomponents.If(hasError,
gm.Alert(gm.AlertDestructive,
gm.AlertTitle(g.Text("Error")),
gm.AlertDescription(g.Text(message)),
),
)
}func UserList(users []User) g.Node {
return Div(
gomponents.Map(users, func(u User) g.Node {
return UserCard(u.Name, u.Email)
}),
)
}Contributions are welcome! Please feel free to submit a Pull Request.
- Built on top of gomponents by @maragudk
- Uses Basecoat UI components and styling (v0.3.10-beta.2)
- Inspired by shadcn/ui
Made with โค๏ธ for the Go community