Who knows how to loop through all sites in multisite installation? I want to create kind of drop down menu to switch between websites. I've found this code inside the backend template but looks like it doesn't works in frontend

{% for site in allSites %}
            <tr data-id="{{ site.id }}" data-name="{{ site.name|t('site') }}">
                <th scope="row" data-title="{{ 'Name'|t('app') }}"><a href="{{ url('settings/sites/' ~ site.id) }}">{{ site.name|t('site') }}</a></th>
                <td data-title="{{ 'Handle'|t('app') }}"><code>{{ site.handle }}</code></td>
                <td data-title="{{ 'Language'|t('app') }}"><code>{{ site.language }}</code></td>
                <td data-title="{{ 'Base URL'|t('app') }}"><code>{{ site.baseUrl }}</code></td>
                {% if multiple %}
                    <td class="thin"><a class="move icon" title="{{ 'Reorder'|t('app') }}" role="button"></a></td>
                    <td class="thin"><a class="delete icon" title="{{ 'Delete'|t('app') }}" role="button"></a></td>
                {% endif %}
        {% endfor %}

UPD: Now I can fetch names of websites by their Ids:

      {% set allSitesIds = craft.app.sites.getAllSiteIds() %}
  {% for site in allSitesIds %}
  {% set siteName = craft.app.sites.getSiteById(site) %}
  {% endfor %}

3 Answers 3


If you want to do this purely from a template, then something like this:

{% set sites = craft.app.sites.getAllSites() %}

{% for site in sites %}
    <li>{{ site.name }}</li>
{% endfor %}

In Craft 3, craft.app in a template basically gives you access to everything in the services namespace. Adding craft.app.sites opens up all of the methods in services/Sites.php



As I responded in the slack, I will respond here in case anyone else has this question.

In the template you can access this information like so:

SiteUrl: {{ siteUrl }}
Langauge: {{ craft.app.language }}
Handle: {{ craft.app.sites.currentSite.handle }}

I'm not entirely positive how to loop the sites in the template but you can get a sites object in PHP by running the following in a controller somewhere or in the init() method of any plugin that is installed:

$sites = Craft::$app->getSites();

This will output the structure of the sites object. Baring this you can look through the craft source / docs to find the details on the site object that returns. There should be a method for getting sites by id or by handle; $sites->getSitesByID() or $sites->getSitesByHandle() but I have not double checked this so you'll have to try it or look it up. If there is not a method then it should be accessible via an array property like $sites->sitesByHandle or $sites->byHandle. Again you'll have to double check on this.

By the way, that functionality should translate to the template version of the sites object craft.app.sites so you should be able to do:

{% set sites = craft.app.sites.getSitesByHandle() %}

Or something like that anyway.

Once you figure it out feel free to update this to reflect the proper methods / properties :)


I created a script in order to save different credentials for each site in my google analytics plugin. So I had to render a dropdown to chose the site as well. Here is my code

First the Plugin variable. You could actually remove the most and just return the sites variable

public function getSites(){
    $response = [];
    $sites = Craft::$app->sites->getAllSites();
    // I had some permission checks here as well to see if the user 
    // has access to the site, but I removed that in this example
    foreach($sites as $site){
        $response[] = array(
            "label" => $site->name,
            "value" => $site->id
    return $response;

And this is my template.

{% set sites = craft.googleanalytics.getSites() %}
{% set siteId = entry.lnkSiteId? entry.lnkSiteId : sites[0]["value"] %}
{{ forms.selectField({
    label:          'Seite'|t,
    id:             'lnkSiteId',
    name:           'lnkSiteId',
    instructions:   'Bitte wählen Sie die Seite'|t,
    value:          siteId,
    options:        sites
}) }}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.