index.php should be the main fallback display template, not the place for every heavy function. WordPress uses index.php as the final fallback in the template hierarchy, and reusable parts should be handled with template parts like get_template_part() so your theme stays clean and child-theme friendly.
Below is an advanced RX Theme index.php with many frontend features: hero area, breadcrumb support, archive/search title, featured post, responsive post grid, schema markup, reading time fallback, categories/tags, comments count, author info, empty state, pagination, sidebar support, accessibility, hooks, and safe escaping.
<?php
/**
* The main template file.
*
* This is the most generic template file in the RX Theme.
* It is used as the final fallback when no more specific template exists.
*
* @package RX_Theme
* @since 1.0.0
*/
defined( 'ABSPATH' ) || exit;
get_header();
/**
* ------------------------------------------------------------
* RX THEME INDEX CONFIG
* ------------------------------------------------------------
*/
$rx_index_layout = apply_filters( 'rx_index_layout', 'right-sidebar' );
$rx_has_sidebar = is_active_sidebar( 'sidebar-1' ) && 'no-sidebar' !== $rx_index_layout;
$rx_main_classes = array(
'rx-site-main',
'rx-index-main',
'rx-layout-' . sanitize_html_class( $rx_index_layout ),
);
$rx_content_classes = array(
'rx-content-area',
$rx_has_sidebar ? 'rx-has-sidebar' : 'rx-no-sidebar',
);
$rx_grid_classes = array(
'rx-post-grid',
$rx_has_sidebar ? 'rx-post-grid--sidebar' : 'rx-post-grid--full',
);
/**
* ------------------------------------------------------------
* SMALL LOCAL FALLBACK HELPERS
* Keep heavy helpers in functions.php/inc/helpers/.
* These are safe fallbacks only.
* ------------------------------------------------------------
*/
if ( ! function_exists( 'rx_index_get_reading_time' ) ) {
/**
* Estimate reading time.
*
* @param int|null $post_id Post ID.
* @return string
*/
function rx_index_get_reading_time( $post_id = null ) {
$post_id = $post_id ? absint( $post_id ) : get_the_ID();
$content = get_post_field( 'post_content', $post_id );
$word_count = str_word_count( wp_strip_all_tags( $content ) );
$minutes = max( 1, ceil( $word_count / 200 ) );
return sprintf(
/* translators: %d: reading time in minutes */
_n( '%d min read', '%d min read', $minutes, 'rx-theme' ),
$minutes
);
}
}
if ( ! function_exists( 'rx_index_get_excerpt' ) ) {
/**
* Get clean excerpt.
*
* @param int $length Excerpt length.
* @return string
*/
function rx_index_get_excerpt( $length = 28 ) {
if ( has_excerpt() ) {
return wp_trim_words( get_the_excerpt(), $length, '...' );
}
return wp_trim_words( wp_strip_all_tags( get_the_content() ), $length, '...' );
}
}
if ( ! function_exists( 'rx_index_schema_blogposting' ) ) {
/**
* Output simple BlogPosting schema for visible post card.
*
* @return void
*/
function rx_index_schema_blogposting() {
$post_id = get_the_ID();
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'BlogPosting',
'headline' => wp_strip_all_tags( get_the_title( $post_id ) ),
'url' => esc_url_raw( get_permalink( $post_id ) ),
'datePublished' => get_the_date( DATE_W3C, $post_id ),
'dateModified' => get_the_modified_date( DATE_W3C, $post_id ),
'author' => array(
'@type' => 'Person',
'name' => get_the_author_meta( 'display_name', get_post_field( 'post_author', $post_id ) ),
),
'publisher' => array(
'@type' => 'Organization',
'name' => get_bloginfo( 'name' ),
),
'mainEntityOfPage' => array(
'@type' => 'WebPage',
'@id' => esc_url_raw( get_permalink( $post_id ) ),
),
);
if ( has_post_thumbnail( $post_id ) ) {
$image = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), 'full' );
if ( ! empty( $image[0] ) ) {
$schema['image'] = esc_url_raw( $image[0] );
}
}
echo '<script type="application/ld+json">' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>';
}
}
?>
<main id="primary" class="<?php echo esc_attr( implode( ' ', $rx_main_classes ) ); ?>">
<?php
/**
* Hook before full index template.
*/
do_action( 'rx_before_index' );
?>
<section class="rx-index-hero" aria-labelledby="rx-index-title">
<div class="rx-container">
<?php
/**
* Breadcrumb.
* Recommended: create rx_breadcrumbs() in inc/helpers/breadcrumbs.php.
*/
if ( function_exists( 'rx_breadcrumbs' ) ) :
?>
<nav class="rx-breadcrumbs-wrap" aria-label="<?php esc_attr_e( 'Breadcrumb', 'rx-theme' ); ?>">
<?php rx_breadcrumbs(); ?>
</nav>
<?php endif; ?>
<header class="rx-index-header">
<?php if ( is_home() && ! is_front_page() ) : ?>
<h1 id="rx-index-title" class="rx-index-title">
<?php single_post_title(); ?>
</h1>
<?php elseif ( is_front_page() ) : ?>
<h1 id="rx-index-title" class="rx-index-title">
<?php echo esc_html( get_bloginfo( 'name' ) ); ?>
</h1>
<?php if ( get_bloginfo( 'description' ) ) : ?>
<p class="rx-index-description">
<?php echo esc_html( get_bloginfo( 'description' ) ); ?>
</p>
<?php endif; ?>
<?php elseif ( is_archive() ) : ?>
<h1 id="rx-index-title" class="rx-index-title">
<?php the_archive_title(); ?>
</h1>
<?php if ( get_the_archive_description() ) : ?>
<div class="rx-index-description">
<?php the_archive_description(); ?>
</div>
<?php endif; ?>
<?php elseif ( is_search() ) : ?>
<h1 id="rx-index-title" class="rx-index-title">
<?php
printf(
/* translators: %s: search query */
esc_html__( 'Search results for: %s', 'rx-theme' ),
'<span>' . esc_html( get_search_query() ) . '</span>'
);
?>
</h1>
<p class="rx-index-description">
<?php esc_html_e( 'Browse the most relevant articles found for your search.', 'rx-theme' ); ?>
</p>
<?php else : ?>
<h1 id="rx-index-title" class="rx-index-title">
<?php esc_html_e( 'Latest Articles', 'rx-theme' ); ?>
</h1>
<p class="rx-index-description">
<?php esc_html_e( 'Evidence-based, simple, and helpful articles from RX Theme.', 'rx-theme' ); ?>
</p>
<?php endif; ?>
</header>
<?php
/**
* Optional search form under hero.
*/
if ( apply_filters( 'rx_index_show_search_form', true ) ) :
?>
<div class="rx-index-search">
<?php get_search_form(); ?>
</div>
<?php endif; ?>
</div>
</section>
<div class="rx-container rx-index-container">
<div class="<?php echo esc_attr( implode( ' ', $rx_content_classes ) ); ?>">
<?php
/**
* Hook before content loop.
*/
do_action( 'rx_before_index_loop' );
?>
<?php if ( have_posts() ) : ?>
<?php
/**
* WordPress main loop.
* have_posts() checks the main WP_Query loop.
*/
$rx_post_counter = 0;
?>
<section class="<?php echo esc_attr( implode( ' ', $rx_grid_classes ) ); ?>" aria-label="<?php esc_attr_e( 'Posts', 'rx-theme' ); ?>">
<?php
while ( have_posts() ) :
the_post();
$rx_post_counter++;
$rx_is_featured = 1 === $rx_post_counter && ! is_paged() && apply_filters( 'rx_index_first_post_featured', true );
$rx_article_classes = array(
'rx-post-card',
'rx-post-card-' . absint( get_the_ID() ),
$rx_is_featured ? 'rx-post-card--featured' : 'rx-post-card--standard',
has_post_thumbnail() ? 'rx-has-thumbnail' : 'rx-no-thumbnail',
);
?>
<article id="post-<?php the_ID(); ?>" <?php post_class( $rx_article_classes ); ?>>
<?php
/**
* Schema per article.
*/
if ( apply_filters( 'rx_index_output_post_schema', true ) ) {
rx_index_schema_blogposting();
}
?>
<?php if ( has_post_thumbnail() ) : ?>
<figure class="rx-post-card__thumbnail">
<a href="<?php the_permalink(); ?>" aria-label="<?php the_title_attribute(); ?>">
<?php
the_post_thumbnail(
$rx_is_featured ? 'large' : 'medium_large',
array(
'class' => 'rx-post-card__image',
'loading' => $rx_is_featured ? 'eager' : 'lazy',
'decoding' => 'async',
'alt' => the_title_attribute(
array(
'echo' => false,
)
),
)
);
?>
</a>
</figure>
<?php endif; ?>
<div class="rx-post-card__body">
<?php
$rx_categories = get_the_category();
if ( ! empty( $rx_categories ) ) :
?>
<div class="rx-post-card__categories" aria-label="<?php esc_attr_e( 'Categories', 'rx-theme' ); ?>">
<?php
foreach ( array_slice( $rx_categories, 0, 3 ) as $rx_category ) :
?>
<a class="rx-post-card__category" href="<?php echo esc_url( get_category_link( $rx_category ) ); ?>">
<?php echo esc_html( $rx_category->name ); ?>
</a>
<?php
endforeach;
?>
</div>
<?php endif; ?>
<header class="rx-post-card__header">
<?php
if ( $rx_is_featured ) :
the_title(
'<h2 class="rx-post-card__title rx-post-card__title--featured"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">',
'</a></h2>'
);
else :
the_title(
'<h2 class="rx-post-card__title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">',
'</a></h2>'
);
endif;
?>
<div class="rx-post-card__meta">
<span class="rx-post-card__meta-item rx-post-card__date">
<time datetime="<?php echo esc_attr( get_the_date( DATE_W3C ) ); ?>">
<?php echo esc_html( get_the_date() ); ?>
</time>
</span>
<span class="rx-post-card__meta-separator" aria-hidden="true">•</span>
<span class="rx-post-card__meta-item rx-post-card__author">
<?php esc_html_e( 'By', 'rx-theme' ); ?>
<a href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>">
<?php echo esc_html( get_the_author() ); ?>
</a>
</span>
<span class="rx-post-card__meta-separator" aria-hidden="true">•</span>
<span class="rx-post-card__meta-item rx-post-card__reading-time">
<?php
if ( function_exists( 'rx_get_reading_time' ) ) {
echo esc_html( rx_get_reading_time( get_the_ID() ) );
} else {
echo esc_html( rx_index_get_reading_time( get_the_ID() ) );
}
?>
</span>
</div>
</header>
<div class="rx-post-card__excerpt">
<p>
<?php
if ( $rx_is_featured ) {
echo esc_html( rx_index_get_excerpt( 42 ) );
} else {
echo esc_html( rx_index_get_excerpt( 26 ) );
}
?>
</p>
</div>
<?php
$rx_tags = get_the_tags();
if ( $rx_is_featured && ! empty( $rx_tags ) ) :
?>
<div class="rx-post-card__tags" aria-label="<?php esc_attr_e( 'Tags', 'rx-theme' ); ?>">
<?php
foreach ( array_slice( $rx_tags, 0, 5 ) as $rx_tag ) :
?>
<a class="rx-post-card__tag" href="<?php echo esc_url( get_tag_link( $rx_tag ) ); ?>">
#<?php echo esc_html( $rx_tag->name ); ?>
</a>
<?php
endforeach;
?>
</div>
<?php endif; ?>
<footer class="rx-post-card__footer">
<a class="rx-post-card__button" href="<?php the_permalink(); ?>" aria-label="<?php echo esc_attr( sprintf( __( 'Read more about %s', 'rx-theme' ), get_the_title() ) ); ?>">
<?php esc_html_e( 'Read More', 'rx-theme' ); ?>
<span aria-hidden="true">→</span>
</a>
<?php if ( comments_open() || get_comments_number() ) : ?>
<span class="rx-post-card__comments">
<?php
comments_popup_link(
esc_html__( 'No comments', 'rx-theme' ),
esc_html__( '1 comment', 'rx-theme' ),
esc_html__( '% comments', 'rx-theme' )
);
?>
</span>
<?php endif; ?>
</footer>
</div>
</article>
<?php endwhile; ?>
</section>
<?php
/**
* Pagination.
* Uses WordPress core pagination.
*/
if ( apply_filters( 'rx_index_show_pagination', true ) ) :
?>
<nav class="rx-pagination" aria-label="<?php esc_attr_e( 'Posts pagination', 'rx-theme' ); ?>">
<?php
the_posts_pagination(
array(
'mid_size' => 2,
'prev_text' => '<span aria-hidden="true">←</span> ' . esc_html__( 'Previous', 'rx-theme' ),
'next_text' => esc_html__( 'Next', 'rx-theme' ) . ' <span aria-hidden="true">→</span>',
'screen_reader_text' => esc_html__( 'Posts navigation', 'rx-theme' ),
)
);
?>
</nav>
<?php endif; ?>
<?php else : ?>
<section class="rx-no-results" aria-labelledby="rx-no-results-title">
<header class="rx-no-results__header">
<h2 id="rx-no-results-title" class="rx-no-results__title">
<?php esc_html_e( 'Nothing Found', 'rx-theme' ); ?>
</h2>
</header>
<div class="rx-no-results__content">
<?php if ( is_search() ) : ?>
<p>
<?php esc_html_e( 'Sorry, no article matched your search. Try different keywords or browse the latest posts.', 'rx-theme' ); ?>
</p>
<?php get_search_form(); ?>
<?php else : ?>
<p>
<?php esc_html_e( 'No posts are available yet. Please check back soon.', 'rx-theme' ); ?>
</p>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
<?php
/**
* Hook after content loop.
*/
do_action( 'rx_after_index_loop' );
?>
</div>
<?php if ( $rx_has_sidebar ) : ?>
<aside id="secondary" class="rx-sidebar rx-index-sidebar" aria-label="<?php esc_attr_e( 'Sidebar', 'rx-theme' ); ?>">
<?php
/**
* Recommended sidebar ID: sidebar-1.
*/
dynamic_sidebar( 'sidebar-1' );
?>
</aside>
<?php endif; ?>
</div>
<?php
/**
* Optional author/info section for home/archive pages.
*/
if ( apply_filters( 'rx_index_show_site_info_box', is_home() || is_front_page() ) ) :
?>
<section class="rx-index-info-box" aria-labelledby="rx-index-info-title">
<div class="rx-container">
<h2 id="rx-index-info-title" class="rx-index-info-box__title">
<?php echo esc_html( apply_filters( 'rx_index_info_title', __( 'About RX Theme', 'rx-theme' ) ) ); ?>
</h2>
<p class="rx-index-info-box__text">
<?php
echo esc_html(
apply_filters(
'rx_index_info_text',
__( 'RX Theme is built for fast, readable, SEO-friendly, and accessible article publishing.', 'rx-theme' )
)
);
?>
</p>
</div>
</section>
<?php endif; ?>
<?php
/**
* Hook after full index template.
*/
do_action( 'rx_after_index' );
?>
</main>
<?php
get_footer();
Important notes
index.php is the final fallback template in WordPress, so this file will work for many pages, but later you should also create more specific files like home.php, archive.php, single.php, page.php, search.php, and 404.php for a professional theme structure. WordPress template hierarchy decides which file loads for each request.
This code uses the main WordPress Loop with have_posts() and pagination through core WordPress functions. That is safer than creating a new custom WP_Query inside index.php for normal blog/archive pages.
Best next file after this is template-parts/content.php, because then your post-card HTML can be reused in home.php, archive.php, and search.php.