Skip to content
Gx
GitHub

Empty

A placeholder for an empty result.

<empty.Empty>
  <empty.EmptyHeader>
    <empty.EmptyMedia variant={empty.Icon}><icons.Info /></empty.EmptyMedia>
    <empty.EmptyTitle>No projects</empty.EmptyTitle>
    <empty.EmptyDescription>Create your first project to start.</empty.EmptyDescription>
  </empty.EmptyHeader>
  <empty.EmptyContent><button.Button>New project</button.Button></empty.EmptyContent>
</empty.Empty>

Installation

Run the command in the app module.

Terminal
gx add empty

The command also installs button and icons.

The command writes these files.

File Path in the app
Empty.gx ui/empty/Empty.gx
EmptyHeader.gx ui/empty/EmptyHeader.gx
EmptyTitle.gx ui/empty/EmptyTitle.gx
EmptyDescription.gx ui/empty/EmptyDescription.gx
EmptyContent.gx ui/empty/EmptyContent.gx
EmptyMedia.gx ui/empty/EmptyMedia.gx
Empty.fixtures.go ui/empty/Empty.fixtures.go
styles.go ui/empty/styles.go
EmptyContent.fixtures.go ui/empty/EmptyContent.fixtures.go
EmptyDescription.fixtures.go ui/empty/EmptyDescription.fixtures.go
EmptyHeader.fixtures.go ui/empty/EmptyHeader.fixtures.go
EmptyMedia.fixtures.go ui/empty/EmptyMedia.fixtures.go
EmptyTitle.fixtures.go ui/empty/EmptyTitle.fixtures.go

Install button and icons first.

Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.

ui/empty/Empty.gx
package empty

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the content of the empty state.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="empty" class={gx.Cx("flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/empty/EmptyHeader.gx
package empty

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the content of the header: the media, the title and the description.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="empty-header" class={gx.Cx("flex max-w-sm flex-col items-center gap-2 text-center", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/empty/EmptyTitle.gx
package empty

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the title text.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="empty-title" class={gx.Cx("text-lg font-medium tracking-tight", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/empty/EmptyDescription.gx
package empty

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the description text.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="empty-description" class={gx.Cx("text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/empty/EmptyContent.gx
package empty

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the content below the header, for example the actions.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="empty-content" class={gx.Cx("flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/empty/EmptyMedia.gx
package empty

props {
  // Variant sets the look of the media: Default or Icon. Icon draws a muted rounded box.
  Variant  Variant = Default
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the media, for example an icon.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="empty-icon" data-variant={p.variant()} class={gx.Cx("mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0", variantClass[Variant(p.variant())], p.Class)} {...p.Attrs}>{p.Children}</div>
ui/empty/Empty.fixtures.go
package empty

import (
	"github.com/alternayte/gx"
	"github.com/alternayte/gx/registry/button"
	"github.com/alternayte/gx/registry/icons"
)

var EmptyFixtures = gx.Fixtures[EmptyProps]{
	"Full": {Children: gx.Frag(
		EmptyHeader(EmptyHeaderProps{Children: gx.Frag(
			EmptyMedia(EmptyMediaProps{Variant: Icon, Children: icons.Info(icons.InfoProps{})}),
			EmptyTitle(EmptyTitleProps{Children: gx.Text("No projects")}),
			EmptyDescription(EmptyDescriptionProps{Children: gx.Text("Create your first project to start.")}),
		)}),
		EmptyContent(EmptyContentProps{Children: button.Button(button.ButtonProps{Children: gx.Text("New project")})}),
	)},
	"Outline": {Class: "border", Children: EmptyHeader(EmptyHeaderProps{Children: gx.Frag(
		EmptyTitle(EmptyTitleProps{Children: gx.Text("No results")}),
		EmptyDescription(EmptyDescriptionProps{Children: gx.Text("Change the filter and search again.")}),
	)})},
}
ui/empty/styles.go
package empty

import "github.com/alternayte/gx"

// Variant is the look of the media slot of an empty state.
type Variant string

// The variants of empty.EmptyMedia.
const (
	Default Variant = "default"
	Icon    Variant = "icon"
)

var variantClass = gx.Enum[Variant]{
	Default: "bg-transparent",
	Icon:    "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6",
}

// variant returns the data-variant value; a zero value is Default.
func (p EmptyMediaProps) variant() string {
	if p.Variant == "" {
		return string(Default)
	}
	return string(p.Variant)
}
ui/empty/EmptyContent.fixtures.go
package empty

import "github.com/alternayte/gx"

var EmptyContentFixtures = gx.Fixtures[EmptyContentProps]{"Empty": {}}
ui/empty/EmptyDescription.fixtures.go
package empty

import "github.com/alternayte/gx"

var EmptyDescriptionFixtures = gx.Fixtures[EmptyDescriptionProps]{"Text": {Children: gx.Text("Create your first project.")}}
ui/empty/EmptyHeader.fixtures.go
package empty

import "github.com/alternayte/gx"

var EmptyHeaderFixtures = gx.Fixtures[EmptyHeaderProps]{"Empty": {}}
ui/empty/EmptyMedia.fixtures.go
package empty

import (
	"github.com/alternayte/gx"
	"github.com/alternayte/gx/registry/icons"
)

var EmptyMediaFixtures = gx.Fixtures[EmptyMediaProps]{
	"Icon":    {Variant: Icon, Children: icons.Info(icons.InfoProps{})},
	"Default": {Children: icons.Info(icons.InfoProps{Class: "size-8"})},
}
ui/empty/EmptyTitle.fixtures.go
package empty

import "github.com/alternayte/gx"

var EmptyTitleFixtures = gx.Fixtures[EmptyTitleProps]{"Title": {Children: gx.Text("No projects")}}

The theme must define these tokens: --muted, --muted-foreground, --foreground, --primary.

Usage

A placeholder for a view with no data.

<empty.Empty>
  <empty.EmptyHeader>
    <empty.EmptyMedia variant={empty.Icon}><icons.Info /></empty.EmptyMedia>
    <empty.EmptyTitle>No projects</empty.EmptyTitle>
    <empty.EmptyDescription>Create your first project to start.</empty.EmptyDescription>
  </empty.EmptyHeader>
  <empty.EmptyContent>
    <button.Button>New project</button.Button>
  </empty.EmptyContent>
</empty.Empty>

The empty state has no visible border. Add class="border" for a dashed outline. EmptyMedia with the Icon variant draws a tile behind the icon.

Examples

Empty: Full

<empty.Empty>
  <empty.EmptyHeader>
    <empty.EmptyMedia variant={empty.Icon}><icons.Info /></empty.EmptyMedia>
    <empty.EmptyTitle>No projects</empty.EmptyTitle>
    <empty.EmptyDescription>Create your first project to start.</empty.EmptyDescription>
  </empty.EmptyHeader>
  <empty.EmptyContent><button.Button>New project</button.Button></empty.EmptyContent>
</empty.Empty>

Empty: Outline

<empty.Empty class="border">
  <empty.EmptyHeader>
    <empty.EmptyTitle>No results</empty.EmptyTitle>
    <empty.EmptyDescription>Change the filter and search again.</empty.EmptyDescription>
  </empty.EmptyHeader>
</empty.Empty>

EmptyContent: Empty

<empty.EmptyContent children={gx.Frag()} />

EmptyDescription: Text

<empty.EmptyDescription>Create your first project.</empty.EmptyDescription>

EmptyHeader: Empty

<empty.EmptyHeader children={gx.Frag()} />

EmptyMedia: Icon

<empty.EmptyMedia variant={empty.Icon}><icons.Info /></empty.EmptyMedia>

EmptyMedia: Default

<empty.EmptyMedia><icons.Info class="size-8" /></empty.EmptyMedia>

EmptyTitle: Title

<empty.EmptyTitle>No projects</empty.EmptyTitle>

API reference

A tag sets a prop by its name with a lower-case first letter: Class is class.

empty.Empty

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the content of the empty state.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

empty.EmptyContent

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the content below the header, for example the actions.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

empty.EmptyDescription

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the description text.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

empty.EmptyHeader

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the content of the header: the media, the title and the description.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

empty.EmptyMedia

Prop Type Default Description
Variant Variant Default Variant sets the look of the media: Default or Icon. Icon draws a muted rounded box.
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the media, for example an icon.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

empty.EmptyTitle

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the title text.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

Do and do not

Do

  • Say what is missing in EmptyTitle.
  • Put the action that fixes the state in EmptyContent.

Don't

  • Do not use an empty state for an error. Use an alert.
  • Do not put more than two actions in EmptyContent.

Keyboard

Key Action
None The component is static.