Search documentation of mob

# Tooling & Formatting

Copy Markdown View Source

Mob ships with first-class support for `mix format` so that `~MOB` sigils are formatted alongside the rest of your Elixir code. Generated projects include a `.formatter.exs` that wires this up automatically.

## Setting up the formatter

Add `Mob.Formatter` to your `.formatter.exs`:

    # .formatter.exs
    [
      plugins: [Mob.Formatter],
      inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
    ]

Then run:

    mix format

That's it. Every `~MOB` sigil in your project is now formatted with the rest of your code in one pass.

## What gets formatted

### Indentation

Children are indented 2 spaces per nesting level, matching Elixir convention:

    # Before
    ~MOB"""
    <Column>
    <Text text="Hello" />
    <Button text="OK" on_tap={tap} />
    </Column>
    """

    # After
    ~MOB"""
    <Column>
      <Text text="Hello" />
      <Button text="OK" on_tap={tap} />
    </Column>
    """

### Attribute wrapping

When a tag's attributes would exceed `line_length`, each attribute moves to its own line with the closing `/>` or `>` on a dedicated line:

    # Short enough to stay inline
    ~MOB(<Button text="Save" on_tap={save_tap} />)

    # Wraps when attributes are too long
    ~MOB"""
    <Image
      source={assigns.avatar_url}
      width={120}
      height={120}
      corner_radius={:radius_pill}
      fill_width={false}
    />
    """

### Expression children

`{expr}` slots are indented to match their sibling nodes:

    ~MOB"""
    <Column>
      <Text text="Items" text_size={:xl} />
      {Enum.map(assigns.items, fn item ->
        ~MOB(<Text text={item} />)
      end)}
    </Column>
    """

## Configuration

### Line length

`Mob.Formatter` respects the standard `line_length` option:

    [
      plugins: [Mob.Formatter],
      line_length: 120,
      inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
    ]

The default is 98 characters.

### CI enforcement

Add `mix format --check-formatted` to your CI pipeline to catch unformatted sigils in the same pass as unformatted Elixir:

    mix format --check-formatted
    mix credo --strict
    mix test

## How it works

`Mob.Formatter` implements the `Mix.Tasks.Format` behaviour introduced in Elixir 1.14. Elixir's formatter calls it for every `~MOB` sigil it encounters, passing the raw sigil content. The formatter re-parses the content using the same `NimbleParsec` grammar as the sigil itself and emits normalised output.

If a sigil can't be parsed (e.g. the file is in a mid-edit incomplete state), the formatter returns the content unchanged rather than raising. `mix format` never breaks a file it can't fully understand.

See `Mob.Formatter` for the full API reference.

## Credo

Projects generated by `mix mob.new` include Credo in dev/test dependencies. Run it with:

    mix credo --strict

The generated `.credo.exs` is pre-configured to work well with Mob's conventions (the `~MOB` sigil, module attribute patterns, etc.).

← Previous Page Testing
Next Page → Publishing to App Store / TestFlight