By Viviana Menzel on Sunday, 20 July 2025
Category: July

How to create an author's page

If your website (or your client’s website) features blogs or articles by multiple authors, you may want to have author pages showing their bio and links to the articles they’ve written. Here’s how to do that with Joomla’s core contact component.

The contact component offers the possibility to create an author’s page displaying not only the contact details, but also a short description of the person and a list of their contributions.

In Joomla it is intended that the author of an article gets their own page using the Contacts Component. For it to work some settings are necessary. You have to create a contact that is linked to the respective user: 

The plugin “Content - Contact” should be activated and pointing to the “Internal contact page”.

You can also activate the plugin "User - Contact Creation" and a contact is automatically created when a user is created. Finally, you need to activate the linking of the author either in the Global Settings for articles or in the corresponding menu item.

Now the author of an article is linked to their contact page:

 

You can edit the contact, upload an image/avatar and fill out all the fields you want to display. In this example I will use the field "More information” to write the author's bio.

To get nice URLs, we create a menu item "Authors" of type "List Contacts in a Category". The menu item can be visible or hidden.

The contact component has many settings, you can display or hide each field one by one. And since it is a contact component, a contact form is usually displayed. In this example I want to display the name, the website, the image, more information and the user articles. That is how the author’s page looks using Cassiopeia:

Make it prettier!

This is not very appealing and not what you imagine for an author’s page. So we will need an override. Create overrides by clicking on System -> Site Templates -> Cassiopeia -> Create overrides. Find com_contact in the list and click on contact. Your overrides are now created in Cassiopeia’s html folder. 

To ensure that it is only for the authors, we will create an alternative layout. That means we have to rename our overrides so we can select them in the menu item. The view for contact consists of several files:

default.php
default-address.php
default articles.php
default-form.php
default.links.php
default profile.php
default-user-custom-fields.php

We rename “default” to “author”. Now we head back to our menu item and select “author” as our layout.

For this example we will only modify author.php and author_articles.php, but you need to keep the other files too.

author.php

I moved some parts around and added Bootstrap classes. The code looks like this:

<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_contact
 *
 * @copyright   (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Contact\Site\Helper\RouteHelper;

/** @var \Joomla\Component\Contact\Site\View\Contact\HtmlView $this */
$tparams = $this->item->params;
$canDo   = ContentHelper::getActions('com_contact', 'category', $this->item->catid);
$canEdit = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by === $this->getCurrentUser()->id);
$htag    = $tparams->get('show_page_heading') ? 'h2' : 'h1';
$htag2   = ($tparams->get('show_page_heading') && $tparams->get('show_name')) ? 'h3' : 'h2';
$icon    = $this->params->get('contact_icons') == 0;
?>

<div class="com-contact contact">
    <?php if ($tparams->get('show_page_heading')) : ?>
        <h1>
            <?php echo $this->escape($tparams->get('page_heading')); ?>
        </h1>
    <?php endif; ?>

    <?php if ($canEdit) : ?>
        <div class="icons">
            <div class="float-end">
                <div>
                    <?php echo HTMLHelper::_('contacticon.edit', $this->item, $tparams); ?>
                </div>
            </div>
        </div>
    <?php endif; ?>

    <?php $show_contact_category = $tparams->get('show_contact_category'); ?>

    <?php if ($show_contact_category === 'show_no_link') : ?>
        <<?php echo $htag2; ?>>
            <span class="contact-category"><?php echo $this->item->category_title; ?></span>
        </<?php echo $htag2; ?>>
    <?php elseif ($show_contact_category === 'show_with_link') : ?>
        <?php $contactLink = RouteHelper::getCategoryRoute($this->item->catid, $this->item->language); ?>
        <<?php echo $htag2; ?>>
            <span class="contact-category"><a href="<?php echo $contactLink; ?>">
                <?php echo $this->escape($this->item->category_title); ?></a>
            </span>
        </<?php echo $htag2; ?>>
    <?php endif; ?>

    <?php if ($tparams->get('show_contact_list') && count($this->contacts) > 1) : ?>
        <form action="#" method="get" name="selectForm" id="selectForm">
            <label for="select_contact"><?php echo Text::_('COM_CONTACT_SELECT_CONTACT'); ?></label>
            <?php echo HTMLHelper::_(
                'select.genericlist',
                $this->contacts,
                'select_contact',
                'class="form-select" onchange="document.location.href = this.value"',
                'link',
                'name',
                $this->item->link
            );
            ?>
        </form>
    <?php endif; ?>

    <?php if ($this->params->get('show_info', 1)) : ?>
        <div class="com-contact-author row gx-5">

            <?php if ($this->item->image && $tparams->get('show_image')) : ?>
                <div class="com-contact-author__image col-md-3">
                    <?php echo LayoutHelper::render(
                        'joomla.html.image',
                        [
                            'src'      => $this->item->image,
                            'alt'      => $this->item->name,
                            'class'    => 'rounded-circle',
                        ]
                    ); ?>
                </div>
            <?php endif; ?>

            <div class="author-info col-md-9">
                <?php if ($this->item->name && $tparams->get('show_name')) : ?>
                    <<?php echo $htag; ?>>
                        <?php if ($this->item->published == 0) : ?>
                            <span class="badge bg-warning text-light"><?php echo Text::_('JUNPUBLISHED'); ?></span>
                        <?php endif; ?>
                        <span class="contact-name"><?php echo $this->item->name; ?></span>
                    </<?php echo $htag; ?>>
                <?php endif; ?>

                <?php if ($this->item->misc && $tparams->get('show_misc')) : ?>

                    <div class="com-contact__miscinfo contact-miscinfo">
                        <?php echo $this->item->misc; ?>
                    </div>
                <?php endif; ?>

                <?php if ($this->item->con_position && $tparams->get('show_position')) : ?>
                    <dl class="com-contact__position contact-position dl-horizontal">
                        <dt><?php echo Text::_('COM_CONTACT_POSITION'); ?>:</dt>
                        <dd>
                            <?php echo $this->item->con_position; ?>
                        </dd>
                    </dl>
                <?php endif; ?>

                <div class="com-contact__info">
                    <?php echo $this->loadTemplate('address'); ?>

                    <?php if ($tparams->get('allow_vcard')) : ?>
                        <?php echo Text::_('COM_CONTACT_DOWNLOAD_INFORMATION_AS'); ?>
                        <a href="<?php echo Route::_('index.php?option=com_contact&view=contact&catid=' . $this->item->catslug . '&id=' . $this->item->slug . '&format=vcf'); ?>">
                        <?php echo Text::_('COM_CONTACT_VCARD'); ?></a>
                    <?php endif; ?>
                </div>
            </div>
        </div>

    <?php endif; ?>

    <?php if ($tparams->get('show_email_form') && ($this->item->email_to || $this->item->user_id)) : ?>
        <?php echo '<' . $htag2 . '>' . Text::_('COM_CONTACT_EMAIL_FORM') . '</' . $htag2 . '>'; ?>

        <?php echo $this->loadTemplate('form'); ?>
    <?php endif; ?>

    <?php if ($tparams->get('show_links')) : ?>
        <?php echo '<' . $htag2 . '>' . Text::_('COM_CONTACT_LINKS') . '</' . $htag2 . '>'; ?>

        <?php echo $this->loadTemplate('links'); ?>
    <?php endif; ?>

    <?php if ($tparams->get('show_articles') && $this->item->user_id && $this->item->articles) : ?>
        <div class="mt-5">
            <?php echo '<' . $htag2 . '>' . Text::_('JGLOBAL_ARTICLES') . '</' . $htag2 . '>'; ?>

            <?php echo $this->loadTemplate('articles'); ?>
        </div>
    <?php endif; ?>

    <?php if ($tparams->get('show_profile') && $this->item->user_id && PluginHelper::isEnabled('user', 'profile')) : ?>
        <?php echo '<' . $htag2 . '>' . Text::_('COM_CONTACT_PROFILE') . '</' . $htag2 . '>'; ?>

        <?php echo $this->loadTemplate('profile'); ?>
    <?php endif; ?>

    <?php if ($tparams->get('show_user_custom_fields') && $this->contactUser) : ?>
        <?php echo $this->loadTemplate('user_custom_fields'); ?>
    <?php endif; ?>

    <?php echo $this->item->event->afterDisplayContent; ?>
</div>

author_articles.php

Here I have added some code to get the intro image, the introtext and the publishing date of the articles. I have again used some Bootstrap classes, so we don’t need to write CSS.

<?php

/**
 * @package     Joomla.Site
 * @subpackage  com_contact
 *
 * @copyright   (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;

/** @var \Joomla\Component\Contact\Site\View\Contact\HtmlView $this */
?>
<?php if ($this->params->get('show_articles')) : ?>
    <div class="com-contact__articles contact-articles">
    <?php foreach ($this->item->articles as $article) : ?>
        <?php
            $images  = json_decode($article->images);
            $layoutAttr = [
                'src' => $images->image_intro,
                'alt' => empty($images->image_intro_alt) && empty($images->image_intro_alt_empty) ? false : $images->image_intro_alt,
            ];
        ?>
        <div class="contact-article row gx-5 py-4 border-bottom">
            <?php if (!empty($images->image_intro)) : ?>
                <div class="contact-image col-md-3">
                    <?php echo LayoutHelper::render('joomla.html.image', array_merge($layoutAttr, ['itemprop' => 'thumbnail'])); ?>
                </div>
            <?php endif; ?>
            <div class="contact-content col-md-9">
                <time class="article-date" datetime="<?php echo HTMLHelper::_('date', $article->publish_up, 'c'); ?>" itemprop="datePublished">
                    <?php echo Text::sprintf(HTMLHelper::_('date', $article->publish_up, Text::_('DATE_FORMAT_LC3'))); ?>
                </time>

                <h3 class="article-title" itemprop="name">
                    <?php echo HTMLHelper::_('link', Route::_(RouteHelper::getArticleRoute($article->slug, $article->catid, $article->language)), $this->escape($article->title)); ?>
                </h3>

                <?php echo $article->introtext; ?>

                <a class="readmore btn btn-secondary" href="<?php echo Route::_(RouteHelper::getArticleRoute($article->slug, $article->catid, $article->language)); ?>" aria-label="<?php echo Text::sprintf('JGLOBAL_READ_MORE_TITLE', $this->escape($article->title)); ?>">
                    <?php echo Text::_('JGLOBAL_READ_MORE'); ?>
                </a>
            </div>
        </div>
    <?php endforeach; ?>
</div>
<?php endif; ?>

The result

Now we have a nice looking page for our authors:

You can see a live example of a similar override on the website of the Joomla associations of the German-speaking countries (Germany, Austria, and Switzerland):

https://www.joomla.de/unsere-autoren/viviana-menzel 

Leave Comments