Empty state

v0.0.10

v0.0.10StableMobStatelessApache-2.0
Installation mix mishka.ui.gen.mob empty_state
Source View Source

The Mob Empty State in Mishka Chelekom shows an indicator, a title, supporting text and optional actions when a list on an iOS or Android screen has nothing in it. It is the native port of the headless Empty State and runs on Mob.

Overview of the Mob Empty State component for iOS and Android

<MishkaEmptyState> draws an indicator (a glyph or emoji), a title, a description and a row of actions. Every part is optional.

align chooses the layout. :center, the default, puts the indicator above the text and fills a blank screen. :leading puts the indicator beside the text, so the empty state sits inside a card or a section without making the screen look broken.

Your screen owns the state. Show the empty state when the list is empty and the list when it has items. The component holds no state and sends no events of its own.

Three slot tags mirror the slots of the web component:

SlotPhoenix slotFalls back to
<MishkaEmptyStateIndicator><:indicator>The indicator glyph prop.
<MishkaEmptyStateActions><:actions>The actions prop, a list of nodes.
Bare children<:inner_block>Nothing. They render after the description.

The actions prop is there for buttons you build from data. When you give both, the slot wins. The component owns the actions row and spaces the buttons for you. Each action hugs its content, because the component sets fill_width to false on it unless you set that attribute yourself.

Generate the empty state component

Run this from your Mob app's root to add the component to lib/<app>/components/:

bash
mix mishka.ui.gen.mob empty_state

The generator registers the tag, so you can use it in ~MOB markup. The examples on this page use the mishka_ prefix of the showcase app (--module-prefix mishka_), so the tag is <MishkaEmptyState>. Without a prefix, the tag is <EmptyState>.

Centered empty state

The default :center layout puts the indicator above the title and the description. Use it when the empty state is the whole screen.

Centered empty state

Empty state example card titled Centred, with a mailbox emoji above the heading No messages and the text Anything you receive lands here, in a white panel
heex
<MishkaEmptyState
  indicator="📭"
  title="No messages"
  description="Anything you receive lands here."
/>

Leading empty state

align={:leading} puts the indicator beside the text. This layout sits inside a card without looking like the screen has failed.

Leading empty state

Empty state example card titled Leading, with a magnifying glass emoji beside the heading No results and the text Try a different search term, in a white panel
heex
<MishkaEmptyState align={:leading} indicator="🔍" title="No results" />

Empty state with actions

Put buttons inside <MishkaEmptyStateActions> and the component lays them out in a row beneath the text, with gaps between them. In this example, tapping New adds a project and Import adds two. Either fills the list, which replaces the empty state.

The buttons are yours, so their handlers are ordinary handle_info/2 clauses. An empty state carries no events of its own.

Empty state with actions

Empty state example card titled With actions, with a folder emoji above No projects yet, the text Create one to get started, and dark New and pale Import buttons
A card listing the projects Imported A and Imported B with a Delete them all button underneath.

Left: the example as it loads. Right: after a tap. Filling the list is what makes the empty state go away.

elixir
# Actions are a SLOT tag, like Chelekom's <:actions>. The component owns
# the row, so it puts the gaps in for you.
<MishkaEmptyState indicator="📁" title="No projects yet"
                  description="Create one to get started.">
  <MishkaEmptyStateActions>
    <Button text="New" on_tap={{self(), :es_new}} />
    <Button text="Import" on_tap={{self(), :es_import}} />
  </MishkaEmptyStateActions>
</MishkaEmptyState>

# The buttons are yours, so the handlers are the ordinary ones. An empty
# state carries no events of its own.
def handle_info({:tap, :es_new}, socket) do
  {:noreply, Mob.Socket.assign(socket, :projects, ["Untitled" | socket.assigns.projects])}
end

def handle_info(_msg, socket), do: {:noreply, socket}

Text-only empty state

Every part is optional. With only a title, the component draws that one line.

Text-only empty state

Empty state example card titled Text only, with just the heading Nothing here in a white panel, in the MishkaMob showcase app on Android
heex
<MishkaEmptyState title="Nothing here" />

Props

PropTypeDefaultDescription
titlestringnilHeading.
descriptionstringnilSupporting text.
align:center or :leading:centerIndicator above the text, or beside it.
indicatorstringnilA glyph or emoji. For an illustration, use the indicator slot.
paddingspacing token or number:space_xlPadding around the block in the centered layout.
actionslist of nodes[]A row beneath the text. Prefer the <MishkaEmptyStateActions> slot.
<MishkaEmptyStateIndicator>slotNoneAn illustration in place of the indicator glyph.
<MishkaEmptyStateActions>slotNoneButtons beneath the text. The component spaces them.

Differences from the web component

  • The id and *_class attributes are not ported.