Chemical Formula

Render chemical formulas with colored element symbols, subscripted amounts, superscripted oxidation states, and configurable element ordering.

Basic Usage

Simple formulas with subscripted amounts and oxidation states:

H 2O C O 2 Na Cl Ca O 2H 2 H 2S O 4 C 6H 12O 6
With oxidation states (use ^2+ or [2+] syntax):
Fe +2 O Fe +3 2O -2 3 Na +1 Cl -1 Ca +2 Cl -2 S O -2 4
  <script>
  import { Formula } from 'matterviz'
</script>

<div style="display: flex; gap: 2em; flex-wrap: wrap; font-size: 1.5em">
  <Formula formula="H2O" />
  <Formula formula="CO2" />
  <Formula formula="NaCl" />
  <Formula formula="Ca(OH)2" />
  <Formula formula="H2SO4" />
  <Formula formula="C6H12O6" />
</div>

<div style="margin-top: 1.5em; font-size: 1.4em">
  With oxidation states (use <code>^2+</code> or <code>[2+]</code> syntax):
</div>

<div style="display: flex; gap: 2em; flex-wrap: wrap; font-size: 1.5em; margin-top: 1em">
  <Formula formula="Fe^2+O" />
  <Formula formula="Fe[3+]2O[2-]3" />
  <Formula formula="Na^+Cl^-" />
  <Formula formula="Ca^2+Cl^-2" />
  <Formula formula="SO4^2-" />
</div>

Element Ordering & Color Schemes

Control element ordering and apply different color schemes:

Ordering:
Color Scheme:
C 6H 12O 6N 2Fe 2
  <script>
  import { Formula } from 'matterviz'

  let formula = $state(`C6H12O6N2Fe2`)
  let ordering = $state(`original`)
  let color_scheme = $state(`Vesta`)
</script>

<label>
  Formula:
  <input type="text" bind:value={formula} style="width: 250px; margin-left: 1em" />
</label>

<div style="display: flex; gap: 1em; flex-wrap: wrap; margin: 1em 0">
  <strong>Ordering:</strong>
  <label><input type="radio" bind:group={ordering} value="original" /> Original</label>
  <label><input type="radio" bind:group={ordering} value="alphabetical" />
    Alphabetical</label>
  <label><input type="radio" bind:group={ordering} value="electronegativity" />
    Electronegativity</label>
  <label><input type="radio" bind:group={ordering} value="hill" /> Hill Notation</label>
</div>

<div style="display: flex; gap: 1em; align-items: center; margin-bottom: 1em">
  <strong>Color Scheme:</strong>
  <select bind:value={color_scheme}>
    <option value="Vesta">Vesta</option>
    <option value="Jmol">Jmol</option>
    <option value="Alloy">Alloy</option>
    <option value="Pastel">Pastel</option>
    <option value="Muted">Muted</option>
    <option value="Dark Mode">Dark Mode</option>
  </select>
</div>

<div
  style="font-size: 2.5em; padding: 1em; background: rgba(255, 255, 255, 0.03); border-radius: 8px"
>
  <Formula {formula} {ordering} {color_scheme} />
</div>

Interactive Click Handlers

Click element symbols to trigger custom actions:

H 2O Fe +3 2O -2 3 Ca O 2H 2 H 2S O 4
Click any element symbol above...
  <script>
  import { Formula } from 'matterviz'

  let clicked_element = $state(null)
  let click_count = $state(0)

  function handle_click(element) {
    clicked_element = element
    click_count++
  }
</script>

<div
  style="display: flex; gap: 2em; flex-wrap: wrap; font-size: 1.8em; margin-bottom: 1em"
>
  <Formula formula="H2O" on_click={handle_click} />
  <Formula formula="Fe[3+]2O[2-]3" on_click={handle_click} />
  <Formula formula="Ca(OH)2" on_click={handle_click} />
  <Formula formula="H2SO4" on_click={handle_click} />
</div>

<div
  style="padding: 1em; background: rgba(0, 150, 255, 0.1); border-radius: 8px; border: 1px solid rgba(0, 150, 255, 0.3)"
>
  {#if clicked_element}
    <strong>Last clicked:</strong> {clicked_element} (Total: {click_count})
  {:else}
    <em>Click any element symbol above...</em>
  {/if}
</div>

Tooltip Positioning

Control tooltip placement and distance from elements:

Side:
Offset: 5px
Fe +3 2O -2 3
  <script>
  import { Formula } from 'matterviz'

  let tooltip_side = $state(`bottom`)
  let tooltip_offset = $state(5)
</script>

<div style="display: flex; gap: 1.5em; align-items: center; margin-bottom: 1em">
  <div>
    <strong>Side:</strong>
    <label><input type="radio" bind:group={tooltip_side} value="top" /> Top</label>
    <label><input type="radio" bind:group={tooltip_side} value="bottom" /> Bottom</label>
    <label><input type="radio" bind:group={tooltip_side} value="left" /> Left</label>
    <label><input type="radio" bind:group={tooltip_side} value="right" /> Right</label>
  </div>

  <div style="display: flex; gap: 0.5em; align-items: center">
    <strong>Offset:</strong>
    <input
      type="range"
      bind:value={tooltip_offset}
      min="0"
      max="30"
      step="5"
      style="width: 100px"
    />
    <span>{tooltip_offset}px</span>
  </div>
</div>

<div
  style="padding: 3em; background: rgba(255, 255, 255, 0.03); border-radius: 8px; text-align: center"
>
  <div style="font-size: 2em; display: inline-block">
    <Formula formula="Fe[3+]2O[2-]3" {tooltip_side} {tooltip_offset} />
  </div>
</div>

As Different HTML Elements

Use the as prop to render formulas as headings, inline text, etc.:

H 2O

C O 2

Inline formulas work too: Na Cl and H 2S O 4 in regular text.

  <script>
  import { Formula } from 'matterviz'
</script>

<Formula as="h1" formula="H2O" style="margin: 0.5em 0" />
<Formula as="h2" formula="CO2" style="margin: 0.5em 0" />

<p style="font-size: 1.1em">
  Inline formulas work too:
  <Formula as="span" formula="NaCl" />
  and
  <Formula as="strong" formula="H2SO4" />
  in regular text.
</p>

Structured Input & Amount Formatting

Use OxiComposition objects and customize number formatting:

From object:
Fe +3 2O -2 3
H 2O C 0
  <script>
  import { Formula } from 'matterviz'

  const iron_oxide = {
    Fe: { amount: 2, oxidation_state: 3 },
    O: { amount: 3, oxidation_state: -2 },
  }

  let amount_format = $state(`.3~s`)
</script>

<div style="display: flex; gap: 2em; align-items: center">
  <div>
    <div style="font-size: 0.9em; color: var(--text-color-muted); margin-bottom: 0.3em">
      From object:
    </div>
    <div style="font-size: 2em">
      <Formula formula={iron_oxide} ordering="alphabetical" />
    </div>
  </div>

  <div style="flex: 1">
    <label>
      Amount Format:
      <select bind:value={amount_format} style="margin-left: 0.5em">
        <option value=".3~s">Compact (default)</option>
        <option value=".2f">2 decimals</option>
        <option value=".0f">Integer</option>
        <option value=".1f">1 decimal</option>
      </select>
    </label>
    <div style="font-size: 1.5em; margin-top: 0.5em">
      <Formula formula="H2.567O1.234C0.891" {amount_format} />
    </div>
  </div>
</div>

Real-World Compounds

Battery materials, minerals, and organic compounds:

Battery Materials

Li Fe P O 4
LiFePO₄ (LFP)
Li Co O 2
LiCoO₂ (LCO)
Li Mn 0Co 0Ni 0O 2
NMC 111

Minerals

Si O 2
Quartz
Fe 3O 4
Magnetite
Ca C O 3
Calcite

Organic

C 6H 12O 6
Glucose
C 8H 10N 4O 2
Caffeine
C 2H 6O
Ethanol
  <script>
  import { Formula } from 'matterviz'

  const categories = {
    'Battery Materials': [
      { name: `LiFePO₄ (LFP)`, formula: `LiFePO4` },
      { name: `LiCoO₂ (LCO)`, formula: `LiCoO2` },
      { name: `NMC 111`, formula: `LiNi0.33Mn0.33Co0.33O2` },
    ],
    'Minerals': [
      { name: `Quartz`, formula: `SiO2` },
      { name: `Magnetite`, formula: `Fe3O4` },
      { name: `Calcite`, formula: `CaCO3` },
    ],
    'Organic': [
      { name: `Glucose`, formula: `C6H12O6` },
      { name: `Caffeine`, formula: `C8H10N4O2` },
      { name: `Ethanol`, formula: `C2H6O` },
    ],
  }
</script>

{#each Object.entries(categories) as [category, compounds]}
  <h3 style="margin: 1.5em 0 0.5em; font-size: 1.1em">{category}</h3>
  <div
    style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1em"
  >
    {#each compounds as { name, formula }}
      <div
        style="padding: 0.8em; border: 1px solid var(--border-color, #ddd); border-radius: 6px"
      >
        <div style="font-size: 1.3em; margin-bottom: 0.4em">
          <Formula
            {formula}
            ordering={category === 'Organic' ? 'hill' : 'electronegativity'}
          />
        </div>
        <div style="font-size: 0.85em; color: var(--text-color-muted)">{name}</div>
      </div>
    {/each}
  </div>
{/each}

Interactive Builder

Build formulas dynamically with live preview:

Fe +3 2O -2 3
  <script>
  import { Formula } from 'matterviz'

  let elements = $state([
    { symbol: `Fe`, amount: 2, oxidation: 3 },
    { symbol: `O`, amount: 3, oxidation: -2 },
  ])

  const formula_obj = $derived.by(() => {
    const obj = {}
    for (const { symbol, amount, oxidation } of elements) {
      obj[symbol] = { amount, oxidation_state: oxidation }
    }
    return obj
  })
</script>

<div style="margin-bottom: 1em">
  <button
    onclick={() => (elements = [...elements, { symbol: `H`, amount: 1, oxidation: 0 }])}
  >
    Add Element
  </button>
</div>

{#each elements as element, idx}
  <div style="display: flex; gap: 0.8em; align-items: center; margin-bottom: 0.5em">
    <input type="text" bind:value={element.symbol} style="width: 50px" placeholder="El" />
    <input
      type="number"
      bind:value={element.amount}
      step="0.1"
      style="width: 70px"
      placeholder="Amt"
    />
    <input
      type="number"
      bind:value={element.oxidation}
      style="width: 60px"
      placeholder="Ox"
    />
    <button onclick={() => (elements = elements.filter((_, i) => i !== idx))}>×</button>
  </div>
{/each}

<div
  style="padding: 1.5em; background: rgba(255, 255, 255, 0.05); border-radius: 8px; font-size: 2.5em; margin-top: 1em"
>
  <Formula formula={formula_obj} />
</div>