Blitz provides a completely custom templating language for use in custom templates. The goal was for templates to always be valid syntax in whatever language you're writing, rather than getting syntax errors in your IDE and having to build the template to see if its output is correct like you do with embedded languages like EJS.
This is the core of any templating language, and this syntax should be
fairly familiar if you've used Handlebars or anything similar. To insert a
variable that's been provided to the template's context argument, surround
it with double underscores: __
.
// Input
export default function __ModelName__Home() {
return <div />
}
// Output
export default function TodosHome() {
return <div />
}
In order to retain our first principle of valid syntax, conditional
expressions currently only work in TypeScript templates. At runtime we
place all of the provided template context values onto process.env
, and
when compiling the template we search for any conditional expressions
against a valid template value on process.env
. This works both for
traditional if
statements as well as ternary operations. Soon we hope to
add support for conditional expressions in JSX for conditional component
insertion as well.
If you access a variable that isn't a template value it is ignored,
allowing functionality like dead code elimination via
process.env.NODE_ENV
into template to work properly.
The conditions are not dynamically evaluated, they're only checked for
truthiness. This means that for dynamic evaluation you'll need to do the
check ahead of time and pass it into the template as context, a check like
if (process.env.componentName === 'SomeComp')
will not work properly.
// Input
export default function __ModelName__Home() {
if (process.env.includeAnalytics) {
useEffect(() => {
setupAnalytics()
}, [])
}
// ...
}
// Output (includeAnalytics: true)
export default function __ModelName__Home() {
useEffect(() => {
setupAnalytics()
}, [])
// ...
}
// Output (includeAnalytics: false)
export default function __ModelName__Home() {
// ...
}
In the case where you need to conditionally insert different JSX into your
templates, the process.env
solution isn't ideal. Because we only do
naive replacement of the inner node, if you embed a process.env
conditional in JSX you'll have something like this:
// input
function MyLink() {
return (
<>
{process.env.useLinkWrapper ? (
<Link>
<a href="//blitzjs.com">Blitz</a>
</Link>
) : (
<a href="//blitzjs.com">Blitz</a>
)}
</>
)
}
// output
function MyLink() {
return (
<>
{
<Link>
<a href="//blitzjs.com">Blitz</a>
</Link>
}
</>
)
}
As you can see, the curly braces that wrap the inner script will persist
in the template output. This is where we can use our custom JSX
conditional syntax. The generator exposes two new JSX elements to TSX
templates: <if condition="value" />
and <else />
. Their behavior is
exactly the same as a standard process.env
conditional. The condition
prop on the if
statement is read out of the template context. If its
value is truthy the first child of <if />
will be rendered, otherwise
the contents of the <else />
element will be rendered.
Both <if />
and <else />
only accept a single child component. You
will see unexpected behavior and errors if you try to provide a fragment
or multiple elements.
// input
function MyLink() {
return (
<if condition="useLinkWrapper">
<Link>
<a href="//blitzjs.com">Blitz</a>
</Link>
<else>
<a href="//blitzjs.com">Blitz</a>
</else>
</if>
)
}
// output
function MyLink() {
return (
<Link>
<a href="//blitzjs.com">Blitz</a>
</Link>
)
}
Interested in helping with the templating language? There's plenty of room for improvement including supporting loops and iteration, actual evaluation of conditionals rather than only truthiness checks, and more! Join us in Discord.