Skip to main content

User Interface

Why React + InertiaJS?​

Modern single-page interactions, rich component ecosystems, and type-safety are now table-stakes for productive web development. By pairing React 18 with InertiaJS, Pagode keeps the simplicity of server-side routing while delivering a fully interactive SPA experience.

  • No separate API layer – Controllers still live in Go; they just return JSON "page props" for React.
  • Reuse the entire npm ecosystem – Charts, editors, drag-and-drop, and every other React package drop right in.
  • Zero client-side routing boilerplate – Inertia intercepts links and form submissions automatically.
  • Typed front-end – Ship confident UIs with TypeScript and shadcn/ui primitives styled by Tailwind v4.
  • Faster iteration – Hot-reload for both Go and React via Vite; no template compilation steps.

Frontend Stack​

Pagode's frontend is built with:

  • React: A declarative library for building interactive UIs
  • Tailwind CSS v4: A utility-first CSS framework for rapid styling
  • shadcn/ui: A beautifully designed component library built on Tailwind and Radix UI
  • InertiaJS: The bridge connecting your Go backend with your React frontend

This combination gives you the best of both worlds: server-side routing with client-side interactivity, all without building a separate API.

Routes and Handlers​

The router functionality is provided by Echo and constructed within the BuildRouter() function inside pkg/handlers/router.go.

A Handler is a simple type that handles one or more of your routes and allows you to group related routes together. All provided handlers are located in pkg/handlers. Handlers also self-register their routes with the router.

Example​

Here's a simple example of creating a new handler:

type Example struct {
orm *ent.Client
}

func init() {
Register(new(Example))
}

func (e *Example) Init(c *services.Container) error {
e.orm = c.ORM
return nil
}

func (e *Example) Routes(g *echo.Group) {
g.GET("/example", e.Page).Name = routenames.Example
g.POST("/example", c.PageSubmit).Name = routenames.ExampleSubmit
}

func (e *Example) Page(ctx echo.Context) error {
// add your code here
}

func (e *Example) PageSubmit(ctx echo.Context) error {
// add your code here
}

Forms​

Building, rendering, validating and processing forms is made extremely easy with Echo binding, validator, and the provided React components.

Start by declaring the form:

type Guestbook struct {
Message string `form:"message" validate:"required"`
form.Submission
}

Embedding form.Submission satisfies the form.Form interface and handles submissions and validation for you.

For form submissions, you can use:

func (e *Example) Submit(ctx echo.Context) error {
var input forms.Guestbook

// Submit the form.
err := form.Submit(ctx, &input)

// Check the error returned, and act accordingly.
switch err.(type) {
case nil:
// All good!
case validator.ValidationErrors:
// The form input was not valid, so re-render the form with the errors included.
return e.Page(ctx)
default:
// Request failed, show the error page.
return err
}

msg.Success(fmt.Sprintf("Your message was: %s", input.Message))

return redirect.New(ctx).
Route(routenames.Home).
Go()
}

Admin Panel​

Pagode includes a dynamic admin panel that provides:

  • A completely dynamic UI to manage all entities defined by Ent
  • A section to monitor all background tasks and queues

Users with admin access will see additional links on the default sidebar. Clicking on the link for any entity type will provide a pageable table of entities and the ability to add/edit/delete.

Code Generation​

In order to automatically provide admin functionality for entities, Pagode uses code generation by leveraging Ent's extension API. A custom extension is provided to generate code that produces flat entity type structs and handler code that work directly with Echo.

Access Control​

Only admin users can access the admin panel. To create an admin user, follow the instructions in the Getting Started section.

Components and Styling​

Pagode uses a combination of server-side components and client-side React components. The frontend styling is powered by Tailwind CSS v4, which provides:

  • A utility-first approach for rapid styling
  • Built-in responsive design
  • Dark mode support
  • Easy customization

The shadcn/ui component library provides beautifully designed, accessible components that are ready to use in your application.