Twig & Shortcut Syntax in the Craft CMS Control Panel

Customize Craft settings with dynamic Twig and shortcut syntax code. Create a better authoring experience and tailor the site setup to your needs.


We usu­al­ly write Twig in our front-end tem­plates; it’s the tem­plat­ing lan­guage that brings the data stored in Craft CMS to the brows­er. How­ev­er, we can use it – and its cousin Craft’s short­cut syn­tax – to dynam­i­cal­ly cus­tomize some set­tings in the Craft CMS con­trol panel.

Can I real­ly use Twig Code in the Craft Con­trol Pan­el? #

Yes, there are spe­cif­ic set­tings input fields in the con­trol pan­el that are mini Twig tem­plates,” as described in the offi­cial Craft doc­u­men­ta­tion, and accept Twig code or Craft’s short­cut syntax. 

It’s the same Twig that you’re used to using in your tem­plates. Use the stan­dard Twig syn­tax for state­ment and out­put tags. You can write ele­ment queries, loop over arrays, use con­di­tion­als, and out­put data. All glob­al vari­ables are also available.

You already use Twig code in your set­tings, but you might not real­ize it. All Craft sec­tions have a Site Set­tings area where you define the Entry URI For­mat. The default URL for­mat is sectionname/{slug}. The {slug} is Craft’s short­cut syn­tax for {{ object.slug }}, which you can also use in the set­tings field. This refers to the slug of the cur­rent entry being saved or accessed.

We can do this because Craft pro­vides these set­tings fields implic­it access to the data of the ele­ment (in this case, an entry) being saved via the object vari­able. This means we can access all prop­er­ties (like title, url, slug) and use them in our Twig code. This opens up many oppor­tu­ni­ties for dynam­i­cal­ly cus­tomiz­ing URLs, titles, and more.

What is the Craft Short­cut Syntax?

Short­cut syn­tax is a more straight­for­ward way to out­put com­mon­ly used data in Craft CMS with­out writ­ing Twig. Craft turns it into Twig before pars­ing it, but it exists to make some com­mon uses sim­pler. Instead of typ­ing the object vari­able that Craft pro­vides, it’s implied. 

One nice­ty with the short­cut syn­tax is that Twig fil­ters are avail­able, so you can manip­u­late the data right inside the curly braces, like to for­mat a date:

{postDate | date('Y')}

In this code exam­ple, the object vari­able is implied, but this code would work exact­ly the same:

{object.postDate | date('Y')}

If you are work­ing with an entry, you might think of this as using entry.title in a sin­gle entry Twig template. 

Should you use the short­cut syn­tax? I don’t think it is nec­es­sary and the char­ac­ters saved aren’t worth the con­fu­sion two dif­fer­ent syn­tax­es (Twig and short­cut) could cause. My rec­om­men­da­tion is to skip the short­cut syn­tax and use stan­dard Twig instead. You can do more, and it match­es what you write in your tem­plates anyway.

Where Can I use Twig in the Craft Con­trol Pan­el? #

The mini Twig tem­plates” are avail­able in the fol­low­ing Craft con­trol pan­el locations:

Cre­at­ing Cus­tom Entry URIs with Twig #

By default, Craft will cre­ate an entry URI that is the sec­tion han­dle (not camel-cased) fol­lowed by the entry slug. E.g. adventures/big-bend. But we can cus­tomize this entry URI for­mat using Twig.

We want our entry URI for­mat to have the cho­sen category’s slug in it as the sec­ond seg­ment of the URI: /adventures/big-bend/some-entry-slug and then fol­lowed by the entry slug. Seg­ment one is hard cod­ed based on the sec­tion name, seg­ment two is the cho­sen cat­e­go­ry slug for that entry, and the third seg­ment is the slug for the entry.

But we don’t know what our cat­e­go­ry will be or what the entry slug will be, so we need to put vari­ables there as place­hold­ers. Those vari­ables will be replaced with the entry data after the entry is created.

Since the loca­tion cat­e­go­ry field data is returned as an array, we want to access it as such. Our Twig code to accom­plish this would be:

adventures/{{ object.locations[0].slug }}/{{ object.slug }}

The first seg­ment with adventures is hard-cod­ed because we’re in a chan­nel sec­tion called Adven­tures,” and I want that in my URL. The code object.locations[0].slug refers to the entry object of the cur­rent­ly saved entry (think about this code run­ning while Craft is sav­ing an entry), and locations are the han­dle for the cat­e­go­ry field in the adven­tures field lay­out. We use the zero index because I want the first (and, in this case, only cat­e­go­ry) from that array of cat­e­gories. Final­ly, the slug prop­er­ty on the loca­tion out­puts the hyphen­at­ed, URL-safe slug for that category.

The end result is that an adven­ture sec­tion entry with the loca­tion cat­e­go­ry of Austin, TX” has the URI adventures/austin-tx/my-grand-adventure.

For the sake of com­plete­ness, here’s the same set­ting but with the short­cut syntax:


What­ev­er you can do in Twig in your tem­plate, you can do in this set­tings field to make your URI exact­ly how you want. You can cus­tomize it with cus­tom field data, post date data, and manip­u­late it with Twig filters.

Dynam­ic Entry Titles with Twig #

In a field lay­out for a Craft CMS sec­tion, we can hide the required Title field and auto­mat­i­cal­ly pop­u­late it with what­ev­er data we want. This is very help­ful when the title won’t be used on the front end but needs to make sense to the con­tent authors. Rather than make con­tent authors input the title field data, we can gen­er­ate it auto­mat­i­cal­ly using Twig.

The Sce­nario

Over in the Real World Craft CMS course, we’re cre­at­ing a site that offers trail run­ning adven­ture trips. Part of the con­tent require­ments for that site is that we have tes­ti­mo­ni­als of past attendees. 

I want to make the con­tent author­ing expe­ri­ence as love­ly as pos­si­ble, elim­i­nat­ing the need to fill out any fields that a machine and soft­ware could do better.

We store the tes­ti­mo­ni­als in a chan­nel sec­tion called Tes­ti­mo­ni­als (han­dle: testimonials). Our tes­ti­mo­ni­als sec­tion has:

  • one default entry type
  • three fields
    • title
    • tes­ti­mo­ni­al copy (plain text)
    • tes­ti­mo­ni­al byline (table field with two columns, Full Name, and Location)

We will dis­able the title field since it’s mean­ing­less to the authors, but it needs to be pop­u­lat­ed with some­thing that makes sense when you view it on the ele­ment list­ing view in the Craft con­trol panel.

We dis­able the title field by uncheck­ing the Show the Title Field” check­box. A new input field appears called Title For­mat,” and this is anoth­er one of our spe­cial mini twig tem­plates. Every entry in Craft needs a title, even if it’s some­thing you hard code. But we will dynam­i­cal­ly cre­ate a title based on entry data (or any glob­al­ly avail­able data via Craft’s glob­als) using Twig code!

We can put any Twig code in this set­tings field, even non­sense like this:

{% for num in 0..3 %} {{num}} {% endfor %}

But I need some­thing mean­ing­ful in this field, like the full name of the tes­ti­mo­ni­al giver. 

The name of the per­son giv­ing the tes­ti­mo­ni­al is stored in the tes­ti­mo­ni­al byline field. This field is a table field, so I auto­mat­i­cal­ly think I need to iter­ate over the rows of data:

{% for byline in object.testimonialByline %} {{byline.fullName }} {% endfor %}

The object vari­able here is the entry data of the entry being saved. Using object.testimonialByline would be the equiv­a­lent of entry.testimonialByline in an indi­vid­ual entry template.

Since we’re just deal­ing with Twig, even the for-loop is unnec­es­sary because we can access the table rows by the array index. In this case, the table field is lim­it­ed to 1 row only. That means we can reli­ably access the row by the index and always get the row I need:

{{ object.testimonialByline[0].fullName }}

This pro­duces the same result!

Final­ly, we can dis­card the object vari­able and just ref­er­ence prop­er­ties of the entry direct­ly because object is implied:

{{ testimonialByline[0].fullName }}

Again, for the sake of com­plete­ness, this will look almost the same in Craft’s short­cut syntax:


Dynam­ic Asset Sub­di­rec­to­ry Names #

In our first exam­ple, we used the loca­tion cat­e­go­ry slug to cre­ate a cus­tom entry URI for­mat. Let’s use that same approach to cre­ate dynam­ic sub­di­rec­to­ries for assets uploaded for an adven­ture entry. If an asset is uploaded with­in an adven­ture entry, and it has the loca­tion cat­e­go­ry slug of austin-tx, then we’ll cre­ate a sub­di­rec­to­ry in our asset vol­ume with the same name and store the file there.

We do this work in the field set­tings for the asset field in the adven­ture default entry type field layout.

We write our Twig code in the set­tings for the Images field under Default Upload Location.”

The Default Upload Loca­tion field accepts both the short­cut and Twig code. Since we have access to all glob­al data and the object vari­able for the cur­rent entry, we can cus­tomize this just as we did the entry URI format.

We can use a for-loop to out­put the cat­e­go­ry slug:

{% for location in object.locations.all() %} {{ location.slug }} {% endfor %}

We can also sim­pli­fy this code to just access the cat­e­go­ry via the array index instead of loop­ing over the array:

{{ object.locations[0].slug }}

And the result is that the file is uploaded to the austin-tx subdirectory:

Go Forth and Cus­tomize #

We didn’t imple­ment all of the set­tings that are avail­able, but you can cus­tomize your URI for­mat, pre­view tar­gets, entry titles, and asset field upload loca­tions. Addi­tion­al­ly, plu­g­ins can offer this same func­tion­al­i­ty and even super­charge it as SEO­mat­ic does with its clever Twig input fields.

Addi­tion­al Resources #