Control Flow

Conditionals, loops, includes, comments, and escaping for dynamic template logic.

Conditionals

Use @if, @elseif, @else, and @endif to conditionally render content:

@if(show_header)
    <header>Welcome back!</header>
@endif

Else and elseif

Chain conditions for more complex logic:

@if(role == "admin")
    <span class="badge">Admin</span>
@elseif(role == "editor")
    <span class="badge">Editor</span>
@else
    <span class="badge">Guest</span>
@endif

Truthiness rules

Conditions are evaluated as truthy or falsy. The following values are considered falsy:

  • false
  • null or undefined variables
  • Empty strings ""
  • The number 0
  • Empty arrays []

Everything else is truthy. This means you can check for the presence of a variable directly:

@if(page.subtitle)
    <p class="subtitle">{{ page.subtitle }}</p>
@endif

Loops

Use @for and @endfor to iterate over arrays:

@for(item in items)
    <li>{{ item }}</li>
@endfor

Accessing properties in loops

When iterating over objects, use dot notation to access their properties:

@for(post in posts)
    <article>
        <h2>{{ post.title }}</h2>
        <time>{{ post.date }}</time>
        <p>{{ post.summary }}</p>
    </article>
@endfor

Combining loops with conditionals

@for(page in pages)
    @if(page.published)
        <a href="{{ page.url }}">{{ page.title }}</a>
    @endif
@endfor

Includes

Use @include to inline another template file. The included file is rendered in place with access to the same variables:

@include('components/nav')

The path is relative to the project root, without the .tulip extension:

<!-- layouts/main.tulip -->
<html>
<head>
    @include('partials/head')
</head>
<body>
    @include('partials/header')
    @yield('content')
    @include('partials/footer')
</body>
</html>

Comments

Template comments are completely stripped from the rendered HTML output:

{{-- TODO: add pagination here --}}

{{-- This entire block is for the beta feature --}}
@if(beta_enabled)
    <div>Beta content</div>
@endif

Unlike HTML comments (<!-- -->), template comments produce no output at all.

Escaping

If you need to output a literal @ character in your template (for example, in an email address), use @@:

Contact us at hello@@example.com

This renders as:

Contact us at hello@example.com

Code blocks

Use @code to embed syntax-highlighted code examples. Content inside is treated as raw text — directives, expressions, and HTML are not processed:

@code('toml')
[site]
name = "My Site"
@endcode

This renders a <pre><code class="language-toml"> block. HTML inside is automatically escaped — no need for &lt; entities. Tulip supports a custom tulip language for template code highlighting.

Verbatim

Use @verbatim to output content without processing any directives or expressions:

@verbatim
{{ this will not be evaluated }}
@if(neither will this)
@endverbatim

Everything between @verbatim and @endverbatim is emitted as plain text.