How to Create a Custom WordPress Archives Page
Older posts inevitably get buried by chronologically newer content. This is especially the case if your website has grown or if you have been blogging for a while now, and your older articles aren’t getting enough notice. Since older articles could still contain evergreen content, WordPress site owners resort to various techniques of bringing attention to them. One of those techniques is simply using the default WordPress archive page.
Archive pages are enabled thanks to a corresponding template file provided by your theme (archive.php file, in most cases). This is often coupled with post sidebars that add various widgets (like search, categories, and tag cloud), which lead to those pages. However, both the default archive page and the post sidebar have their flaws.
The default archive pages often only show a list of posts based on a taxonomy without giving additional information about the posts. On the other hand, long sidebars can often cause a sense of clutter on a post and are overall bad for the mobile website experience. Because of this, some site owners opt to create a custom WordPress archive page. In this article, we will give our take on how to create a page like that.
How to create a custom WordPress archives page
Now that you know why a custom archives page could be helpful, let us see how you can create it. For this article, we have created a custom archive page using code. Given the use of coding, this task is better suited for more advanced WordPress users. However, we will also explain the code we use, part by part, so that a wider audience can understand it. We created an example that should be instructive and easy for you to use or improve upon, depending on your WordPress comfort level.
As with our previous articles, we strongly suggest creating a backup of your website before going any further. This will ensure that no harm is done to your website and give you peace of mind in going forward. Once you’ve done so, we also advise reviewing our article on the use of FTP, as knowing how to use FTP is crucial for some of the steps described below. With that being said, let us proceed.
The code that we created to make our custom WordPress archive page is given below.
<?php /* Template Name: Custom Archive Template */ get_header(); ?> <main id="main" class="content-wrapper"> <?php if ( have_posts() ) { while ( have_posts() ) { the_post(); ?> <h1 class="page-title"><?php echo esc_html( get_the_title() ); ?></h1> <div class="page-section"> <div class="page-content"> <?php the_content(); ?> </div> <?php } wp_reset_postdata(); } ?> <div class="archive-sidebar"> <div class="archive-categories"> <p><strong><?php echo esc_html__( 'Categories', 'textdomain' ); ?></strong></p> <ul class="category-list"> <?php wp_list_categories( array( 'title_li' => '', 'hide_title_if_empty' => true ) ); ?> </ul> </div> <div class="archive-tags"> <p><strong><?php echo esc_html__( 'Tags', 'textdomain' ); ?></strong></p> <?php wp_tag_cloud(); ?> </div> <div class="archive-authors"> <p><strong><?php echo esc_html__( 'Authors', 'textdomain' ); ?></strong></p> <?php wp_list_authors( array( 'hide_empty' => 'true', 'optioncount' => 'true' ) ); ?> </div> </div> </div> <?php $paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1; $posts_query = new WP_Query( array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 8, 'paged' => $paged ) ); ?> <div class="posts-section"> <?php if ( $posts_query->have_posts() ) { ?> <h2><?php echo esc_html__( 'Our latest work', 'textdomain' ); ?></h2> <div class="archived-posts"> <?php while ( $posts_query->have_posts() ) { $posts_query->the_post(); ?> <div class="archive-item"> <?php if ( has_post_thumbnail( get_the_ID() ) ) { ?> <div class="post-thumbnail"> <a href="<?php the_permalink(); ?>"> <?php the_post_thumbnail(); ?> </a> </div> <?php } ?> <div class="post-title"> <a href="<?php the_permalink(); ?>"> <h3><?php the_title(); ?></h3> </a> </div> </div> <?php } ?> </div> <?php $total_pages = $posts_query->max_num_pages; if ( $total_pages > 1 ) { $current_page = max( 1, get_query_var( 'paged' ) ); ?> <div class="archive-pagination"> <?php echo paginate_links( array( 'base' => get_pagenum_link( 1 ) . '%_%', 'format' => 'page/%#%', 'current' => $current_page, 'total' => $total_pages ) ); ?> </div> <?php } wp_reset_postdata(); } else { ?> <div class="archived-posts"><?php echo esc_html__( 'No posts matching the query were found.', 'textdomain' ); ?></div> <?php } ?> </div> </main> <?php get_footer();
Now, let us explain it in more detail. The first step in doing so is to examine it in a very simplified form.
<?php /* Template Name: Custom Archive Template */ get_header(); ?> <main id="main" class="content-wrapper"> <!-- Some code goes here--> </main> <?php get_footer();
By reviewing the template header comment, you can conclude that the code represents a custom page template called Custom Archive Template. Since no additional information is added, this template will be available only for pages and can be selected as a template within the Page Attributes section on a given page.
Apart from that, by using the get_header() and get_footer() functions, we are loading the header and footer templates of the currently active theme. The remaining code is used to create the main section of the page.
The main part of the page content consists of three subsections. The first section shows the page title and the content that was inserted in the page editor. The title is displayed thanks to the get_the_title() function, while the page editor content is shown by placing the the_content() function within a WordPress Loop.
<?php if ( have_posts() ) { while ( have_posts() ) { the_post(); ?> <h1 class="page-title"><?php echo esc_html( get_the_title() ); ?></h1> <div class="page-section"> <div class="page-content"> <?php the_content(); ?> </div> <?php } wp_reset_postdata(); } ?> <!-- Sidebar code goes here--> </div>
The second section, which is designed as a sidebar, contains the links to the default WordPress archive pages that show categories, tags, and authors. We have purposely included only those three, as adding too much content might make the page cluttered. However, you can opt to include additional content like custom widget areas or links to other archive pages.
With that being said, each of the aforementioned three parts is preceded by a small paragraph noting what they are (Categories, Tags, and Authors, respectively). The category links are shown thanks to the wp_list_categories() function, which, as the name suggests, shows a list of category names. As we already used a small paragraph titled Categories, we didn’t want to include an additional title for that category list, so we set the title_li attribute to an empty string. Also, we have opted to hide any empty categories, i.e. categories that don’t have any posts assigned to them.
The tags are simply shown calling the wp_tag_cloud() function, without specifying any parameters.
Finally, we have shown the authors by calling the wp_list_authors() function. Additionally, we chose to hide any authors without posts and to show how many posts each author has created. This will be shown within parenthesis, after the author’s name.
<div class="archive-sidebar"> <div class="archive-categories"> <p><strong><?php echo esc_html__( 'Categories', 'textdomain' ); ?></strong></p> <ul class="category-list"> <?php wp_list_categories( array( 'title_li' => '', 'hide_title_if_empty' => true ) ); ?> </ul> </div> <div class="archive-tags"> <p><strong><?php echo esc_html__( 'Tags', 'textdomain' ); ?></strong></p> <?php wp_tag_cloud(); ?> </div> <div class="archive-authors"> <p><strong><?php echo esc_html__( 'Authors', 'textdomain' ); ?></strong></p> <?php wp_list_authors( array( 'hide_empty' => 'true', 'optioncount' => 'true' ) ); ?> </div> </div>
The third section, thanks to the WP_Query class and a custom query, simply shows all the published posts on your website, starting from the most recent. As your website could have numerous posts, we made it so that this section only shows eight posts per page. It will also have numeric pagination at the bottom, which will help visitors navigate through all the posts. Furthermore, by using the get_query_var() function, we made sure to properly check and display posts from a given page only.
Since the given template is already rich in content, we tried to keep the contents of this section simple. Therefore, if there are published posts available, they will be shown alongside an h2 heading saying Our latest work. If there are no published posts, a label stating No posts matching the query were found. will be shown instead. The posts are represented by their titles and featured images (if the posts have featured images included). The latter is achieved by using the has_post_thumbnail() conditional statement. Apart from that, both the title and featured image link to the page of the appropriate post, thanks to the use of the_permalink() function. Finally, all labels within this and previous sections are properly escaped for security reasons using the esc_html() function and are made translatable.
<?php $paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1; $posts_query = new WP_Query( array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 8, 'paged' => $paged ) ); ?> <div class="posts-section"> <?php if ( $posts_query->have_posts() ) { ?> <h2><?php echo esc_html__( 'Our latest work', 'textdomain' ); ?></h2> <div class="archived-posts"> <?php while ( $posts_query->have_posts() ) { $posts_query->the_post(); ?> <div class="archive-item"> <?php if ( has_post_thumbnail( get_the_ID() ) ) { ?> <div class="post-thumbnail"> <a href="<?php the_permalink(); ?>"> <?php the_post_thumbnail(); ?> </a> </div> <?php } ?> <div class="post-title"> <a href="<?php the_permalink(); ?>"> <h3><?php the_title(); ?></h3> </a> </div> </div> <?php } ?> </div> <?php // Pagination code goes here wp_reset_postdata(); } else { ?> <div class="archived-posts"><?php echo esc_html__( 'No posts matching the query were found.', 'textdomain' ); ?></div> <?php } ?> </div>
Now that we’ve explained that, the only thing that we need to elaborate on is the pagination code. The pagination was done using the paginate_links() function, which can be used to create a paginated list of links. Also, we included additional code that ensures that the pagination is only shown in the cases where there are enough posts.
More precisely, we accessed the max_num_pages attribute of the custom query named $posts_query that we created previously. Thanks to it, the navigation is only shown if the number of pages is greater than one. Since we have set the number of posts per page to eight earlier, it means that the pagination is only shown if there are more than eight posts available, which is the expected behavior.
As for the paginate_links() function, we only used the following parameters—base, format, current, and total. The base represents the main part of the pagination URL. We have opted to use the get_pagenum_link(1), which represents the link of the first page, i.e. the page that has the custom template enabled. We also appended %_%, which will be replaced with the format parameter.
And for the format, we put page/%#%, where the %#% part will be replaced with the appropriate pagination number. As an example, if we want to access the second page from the pagination, its URL will be current-page-URL/page/2 thanks to the chosen format.
Additionally, we have to mention that, in our case, the URL of the current page ends with a trailing slash, due to the permalink choice we made on the website. In the case you are using a permalink setting that doesn’t include a trailing slash, you would need to adjust the format. Two solutions that come to mind are either /page/%#% or ?page/%#%.
With that being said, the remaining two parameters we used are quite straightforward—total contains the number of links that will be shown and current properly keeps track of which page in the pagination a visitor is currently on.
$total_pages = $posts_query->max_num_pages; if ( $total_pages > 1 ) { $current_page = max( 1, get_query_var( 'paged' ) ); ?> <div class="archive-pagination"> <?php echo paginate_links( array( 'base' => get_pagenum_link( 1 ) . '%_%', 'format' => 'page/%#%', 'current' => $current_page, 'total' => $total_pages ) ); ?> </div> <?php }
This concludes our explanation of the custom code we created.
However, there is a lot more to cover on how to properly store and use this template. We will delve into that below.
Since the code represents a custom page template, you will need to put it in a separate file and upload it, via FTP, to your server in one of four possible places. As mentioned in our article on custom page templates, those 4 places are the directories of your parent or child theme, depending on which you are using, or a subdirectory within either of the two. When creating this example for the article, we used the first option.
Also, you should carefully choose how you name the file. Avoid using the prefix page- when naming it. Instead, you should name the file so that its purpose is clear. For example, we named our file custom-archive-template.php.
Once you upload the file to your server, you can create a new page on your website and select your newly created page template on it. Then, you can add the content using your preferred page editor. This content will appear in the first of the three sections we previously mentioned.
Afterward, examine the output of your page template. Thanks to your theme’s existing stylization, this content should also be partially styled to match. However, you will probably need additional CSS code to further adjust the page style to your liking.
The creation of this CSS should be done on a case-by-case basis, which is why we can’t provide a definitive solution or supply you with a universal one. Nevertheless, we will share the CSS we created for our example in the hopes you might find parts of it useful and can adjust it to your website. We will also cover the correct programming practices for storing that CSS.
If you only need a bit of CSS to get the job done, inserting it in Appearance > Customize > Additional CSS is an acceptable solution. However, enqueueing your CSS using the wp_enqueue_style() function is the proper programming practice, especially if the amount of CSS is larger. As this was the case on our end, we decided to show you the PHP code we used for enqueueing the CSS styles as well. This code can be inserted either inside the functions.php file of your theme or within a site-specific plugin. Additionally, for the enqueueing to work properly, you need to create the .css file with the appropriate stylization first and upload it to your server.
Generally speaking, you can upload this .css file in any of the previously mentioned four locations. But, if you plan on making further page templates and subsequent .css files for their stylization, you should consider placing the templates inside a subfolder of your current theme folder. Furthermore, creating a nested folder structure within that subfolder is something you should consider for separating all the files (.css, .js, and .php) within it.
However, since we are only making this as an example, we’ve uploaded our .css file in the same place as the template file—within the theme folder. The file name that we used was custom-archive-template-style.css.
Taking all that into account, the appropriate PHP code for enqueueing our .css file is shown below.
if ( ! function_exists( 'custom_archive_template_styles' ) ) { function custom_archive_template_styles() { if ( is_page_template( 'custom-archive-template.php' ) ) { wp_enqueue_style( 'custom-archive-template-style', get_template_directory_uri() . '/custom-archive-template-style.css' ); } } } add_action( 'wp_enqueue_scripts', 'custom_archive_template_styles' );
Let’s quickly review this code as well. First of all, it represents a function called custom_archive_template_styles() that is “hooked” onto a wp_enqueue_scripts hook. This hook is used for enqueueing both the frontend scripts and the styles.
The function contains a single conditional—is_page_template() that determines if the specified template is being used and if the condition is met for the enqueueing code to be executed. Simply put, if the template path is given properly, it means that you enqueue the style only on the frontend of a page that uses the specified page template.
We used two parameters to enqueue the .css file using the wp_enqueue_style() function—a unique handle and a path to the .css file. The path is created using the get_template_directory_uri() function, which retrieves the URL of the folder of your parent theme, without the trailing slash. Without being said, we will clarify some potential doubts a reader could have regarding this code before going any further.
First of all, if you have decided to place the template file within a subfolder, you will need to adjust the relative path used when calling the is_page_template() function to ‘subfolder-name/template-file-name.php’. Needless to say, the subfolder-name and template-file-name.php parts should be replaced with the actual names of your subfolder and template file, respectively.
Additionally, if you are using a child theme, you should replace the get_template_directory_uri() function with get_stylesheet_directory_uri(), which returns the URL of your child theme, without the trailing slash. Finally, if your .css file is placed inside a nested subfolder structure, make sure to properly write the remaining part of its URL after using the appropriate function of the two previously mentioned.
Having said that, the CSS we used is shown below. But, since it is created on a case-by-case basis, the elements we used might not apply to you, so we will not go into in-depth explanations as we’ve done for the code above. In case you aren’t familiar with some of the CSS rules we used, we advise reading up on them before proceeding.
.page-section { display: grid; } @media only screen and (min-width: 1025px) { .page-section { grid-template-columns: auto 25%; grid-gap: 2rem; } } .page-content, .archive-sidebar { margin-bottom: 20px; } .archived-posts { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); grid-gap: 1.2rem; } .post-title { padding: 6px 0; } .archive-pagination { position: relative; text-align: center; margin: 35px 0 0; } .page-numbers { margin: 0 10px; font-size: 17px; font-weight: 600; color: #333; border-radius: 50%; line-height: 32px; } .page-numbers.current { background: #e82a2a; padding: 0 9px; } ul.category-list li { display: inline-block; } ul.category-list li, .archive-authors li { list-style: none; } ul.category-list li a { margin: 0 10px 5px 0; float: left; background: #eee; color: #333; padding: 5px 10px; } ul.category-list li a:hover { background: #e82a2a; color: #fff; }
After inserting this CSS, we got the output you can see in the screenshot below.
Final Thoughts
Having a custom archives page can help bring new readers to your posts, especially to the older ones. Additionally, it is a great tool that can help visitors navigate through the various parts of your blog while giving more insight into it. Using a custom WordPress archive page alongside the regular menu navigation can also increase the user experience and eliminate the feeling of being stranded on default archive pages.
As this subject can be explored in various ways, we hope that our example inspires you to create your unique archives page. The code we put together should help you create your Archives page with ease. And, as we included some advanced coding points we hope this article could prove useful in your future coding endeavors.