Elixir,Technology

Sigil Sorcery: Custom Sigils in Elixir

Everyone knows sigils like ~s, ~r and ~w. This post shows how to build your own, with and without modifiers, step by step.

Elixir sigils are general-purpose tools for handling common data types. Examples of built-in sigils include strings (~s), regular expressions (~r), dates (~D), and lists of words (~w). But, as you might have guessed, Elixir’s flexibility doesn’t stop here; Elixir allows us to create custom sigils for specific needs, and that is what we’re doing now.

Feeling creative? The process of creating your own sigils

Conceptually, a sigil is a function that takes arguments like functions do. Sigil function names follow a specific syntax, as we can see below (cf. step 1). Unlike in other functions, we are not limited to the usual delimiters () around the function’s parameters but have 8 different delimiters we can freely choose from (cf. step 2). Finally, the parameters of a sigil function comprise the input string and a list of modifiers (cf. step 3).

How to Build It: A Step-by-Step Recipe

  1. Define a function named sigil_x (replace ‘x’ with your character). The sigil name should be either a one-letter lowercase letter or a sequence of uppercase letters.

  2. Pick a delimiter of your choosing: ~r(hello) ~r/hello/ ~r|hello| ~r"hello" ~r'hello' ~r[hello] ~r{hello} ~r<hello>.
    Pro tip: Although ~s/https:/// and ~s(https://) yield the same result, I find the latter expression to be (far) more readable.

# Different syntax, same result:
iex(14)> ~s/https:///    
"https://"
iex(15)> ~s(https://)
"https://"
  1. A sigil function takes two arguments: your input and a list of modifiers. Modifiers are optional. So we get the following function signature: sigil_x(string, modifiers \ []).

  2. Use your sigil function in your template or in iex like so: `<%= ~x(string) %>` and with a modifier: `<%= ~x(string)m %>`.

Examples of Custom Sigils WITHOUT a modifier

Without a modifier, our sigil function will simply return the modified input string. Let’s see how this works in practice:

Example 1 without a modifier

defmodule YourProject.SigilSorcery do
  def sigil_UPREV(string, _modifier) do
    String.upcase(string)
    |> String.reverse()
  end
end

Boom, you’ve got your personalised sigil!

What we do here is take the input string, upcase it, and reverse it. Now, let’s see how to use it:

import YourProject.SigilSorcery
iex(92)> ~UPREV(hello)
"OLLEH"

Example 2 without a modifier

defmodule YourProject.EmojiSigil do
  @emoji_map %{
    "star" => "⭐",
    "sun" => "🌞",
    "moon" => "🌙",
    "planet" => "🪐",
    "rocket" => "🚀",
  }
  def sigil_EMO(string, _modifiers) do
    String.split(string)
    |> Enum.map(&replace_with_emoji/1)
    |> Enum.join(" ")
  end
  defp replace_with_emoji(word) do
    Map.get(@emoji_map, word, word)
  end
end

What we do here is take the input string, split it into a list of words, and replace each word with an emoji if it exists in our emoji map. If the word is not in our map, we return the word as is. Then, we return the list of emojis joined into a string.

import YourProject.EmojiSigil
iex(92)> ~EMO{star sun moon planet rocket}
"⭐ ☀️ 🌙 🪐 🚀"
iex(93)> ~EMO{hello star hello sun and moon}
"hello ⭐ hi ☀️ and see you 🌙"			

Examples of Custom Sigils WITH a modifier

Modifiers are optional. If you want to use them, you need to define them in the function signature. Modifiers are passed as a list of strings. As mentioned above, You call this function like so: `~x(string)m`. Let’s see how this works in practice:

Example 1 with a modifier

defmodule YourProject.SigilCase do
  def sigil_STR(string, 'u'), do: String.upcase(string)
  def sigil_STR(string, 'l'), do: String.downcase(string)
  def sigil_STR(string, 'r'), do: String.reverse(string)
end

What we do here is create three function clauses, each of which takes a string and a modifier. If the modifier is ‘u’, we upcase the string. If the modifier is ‘l’, we downcase the string. If the modifier is ‘r’, we reverse the string.


import YourProject.SigilCase&amp;lt;br>iex(92)> ~STR(hello)u&amp;lt;br>"HELLO"&amp;lt;br>iex(93)> ~STR(HellO)l&amp;lt;br>"hello"&amp;lt;br>iex(94)> ~STR(hello)r&amp;lt;br>"olleh"

			

Example 2 with a modifier

defmodule YourProject.ZooSigil do
  def sigil_ZOO(string, modifiers) do
    repeat_count = String.to_integer(List.to_string(modifiers))
    String.split(string)
    |> Enum.map(&replace_with_animal(&1, repeat_count))
    |> Enum.join(" ")
  end
  defp replace_with_animal(word, repeat_count) do
  emoji=
    case word do
      "cat" -> "🐱"
      "dog" -> "🐶"
      "bird" -> "🐦"
      _ -> word
    end
    String.duplicate(emoji, repeat_count)
  end
end

What we do here is take the input string, split it into a list of words, and replace each word with an emoji if it exists in our emoji map. If the word is not in our map, we return the word as is. Then, we return the emoji repeated n times, where n is the modifier passed to the sigil function. Finally, we join the list of emojis into a string.

import YourProject.ZooSigil&amp;lt;br>iex(92)> ~ZOO(cat dog bird)3&amp;lt;br>"🐱🐱🐱 🐶🐶🐶 🐦🐦🐦"

Conclusion: Embrace the Sigil Sorcery

Sigils are handy little tools; they’re a mix between optimisation of efficiency and a playground for creativity. So, whether you’re regexing, listifying, or just having fun building your own sigils, sigils are a worthy addition to your coding journey. Go ahead, wield these spells with a smile, and watch your Elixir code transform from ordinary to enchanting ☺️.

Basics and background on the Elixir programming language

IEx is awesome!

More articles

Inspired at Eplan Next 2026

Tandem presentation with EPLAN on engineering data management and insights into the Squid Data Hub.

Read more →

From Online Calls to Real Connections: Sarah and Jerome’s German Adventure

Sarah and Jerome from Onja travelled from Madagascar to Germany and finally got to meet their team in person.

Read more →

Inspired Consulting Introduces Squid Data Hub

Inspired Consulting introduces Squid Data Hub – a software created to manage engineering data like cables, pipes and more.

Read more →

Why the Design Handover Isn’t the End

The design handover isn't the finish line, it's the start. Why design keeps running after the Figma files are passed on.

Read more →

Squid Data Hub debuts at Nor-Shipping

Squid Data Hub goes public at Nor-Shipping 2025 — connecting electrical engineering and system integration for better project data management.

Read more →

Comparing Spring Boot and Phoenix

Why we use both frameworks: Phoenix when we run the solution, Spring Boot when the client's team takes over later.

Read more →