page.php

I’ll build this as an advanced WordPress page.php template for your RX Theme, with SEO-friendly structure, accessibility, breadcrumbs, featured image, TOC, child pages, pagination, comments, schema hooks, and safe fallbacks.

Below is an advanced page.php for your RX Theme. It is safe, SEO-friendly, accessible, and includes many possible page-template features with fallback checks so it will not break if some helper functions do not exist.

Create this file here:

rx-theme/page.php

Then paste this code:

<?php
/**
 * The template for displaying all static pages.
 *
 * File: page.php
 *
 * RX Theme Advanced Page Template
 *
 * Features included:
 * - Secure direct-access protection
 * - Header and footer loading
 * - Schema-ready wrapper
 * - Skip link friendly structure
 * - Breadcrumb support
 * - Hero section
 * - Featured image support
 * - Page title
 * - Subtitle/excerpt support
 * - Author/date modified meta option
 * - Reading time support
 * - Table of contents support
 * - Main page content
 * - Page pagination
 * - Child pages listing
 * - Related pages/posts support
 * - Previous/next page navigation
 * - Comments support
 * - Sidebar support
 * - Edit link for admins
 * - Hooks for future extension
 *
 * @package RX_Theme
 */

defined( 'ABSPATH' ) || exit;

get_header();

/**
 * Helpful variables.
 */
$rx_page_id          = get_the_ID();
$rx_show_sidebar    = is_active_sidebar( 'sidebar-page' );
$rx_has_thumbnail   = has_post_thumbnail();
$rx_page_classes    = array( 'rx-page-template', 'rx-page-single' );
$rx_container_class = $rx_show_sidebar ? 'rx-container rx-layout rx-layout-sidebar' : 'rx-container rx-layout rx-layout-full';

/**
 * Add custom body-like classes for the page wrapper.
 */
if ( $rx_has_thumbnail ) {
	$rx_page_classes[] = 'rx-page-has-thumbnail';
} else {
	$rx_page_classes[] = 'rx-page-no-thumbnail';
}

if ( $rx_show_sidebar ) {
	$rx_page_classes[] = 'rx-page-with-sidebar';
} else {
	$rx_page_classes[] = 'rx-page-no-sidebar';
}

/**
 * Optional custom page settings.
 *
 * You can later create these custom fields:
 * - rx_hide_page_title = 1
 * - rx_hide_featured_image = 1
 * - rx_show_page_meta = 1
 * - rx_show_child_pages = 1
 * - rx_show_related_content = 1
 * - rx_page_subtitle = custom subtitle text
 */
$rx_hide_title          = get_post_meta( $rx_page_id, 'rx_hide_page_title', true );
$rx_hide_featured_image = get_post_meta( $rx_page_id, 'rx_hide_featured_image', true );
$rx_show_page_meta      = get_post_meta( $rx_page_id, 'rx_show_page_meta', true );
$rx_show_child_pages    = get_post_meta( $rx_page_id, 'rx_show_child_pages', true );
$rx_show_related        = get_post_meta( $rx_page_id, 'rx_show_related_content', true );
$rx_custom_subtitle     = get_post_meta( $rx_page_id, 'rx_page_subtitle', true );

/**
 * RX before page hook.
 */
do_action( 'rx_before_page_template' );
?>

<main id="primary" class="<?php echo esc_attr( implode( ' ', $rx_page_classes ) ); ?>" role="main">

	<?php
	/**
	 * RX before page main hook.
	 */
	do_action( 'rx_before_page_main' );
	?>

	<?php if ( function_exists( 'rx_breadcrumbs' ) ) : ?>
		<section class="rx-breadcrumb-section" aria-label="<?php esc_attr_e( 'Breadcrumb', 'rx-theme' ); ?>">
			<div class="rx-container">
				<?php rx_breadcrumbs(); ?>
			</div>
		</section>
	<?php endif; ?>

	<?php while ( have_posts() ) : ?>
		<?php the_post(); ?>

		<article id="post-<?php the_ID(); ?>" <?php post_class( 'rx-page-article' ); ?> itemscope itemtype="https://schema.org/WebPage">

			<?php
			/**
			 * RX before page article hook.
			 */
			do_action( 'rx_before_page_article', $rx_page_id );
			?>

			<header class="rx-page-header">

				<div class="rx-container">

					<?php if ( empty( $rx_hide_title ) ) : ?>
						<div class="rx-page-title-wrap">

							<?php
							/**
							 * Optional parent page label.
							 */
							$rx_parent_id = wp_get_post_parent_id( $rx_page_id );

							if ( $rx_parent_id ) :
								?>
								<p class="rx-page-parent-label">
									<a href="<?php echo esc_url( get_permalink( $rx_parent_id ) ); ?>">
										<?php echo esc_html( get_the_title( $rx_parent_id ) ); ?>
									</a>
								</p>
							<?php endif; ?>

							<h1 class="rx-page-title" itemprop="headline">
								<?php the_title(); ?>
							</h1>

							<?php if ( ! empty( $rx_custom_subtitle ) ) : ?>
								<p class="rx-page-subtitle">
									<?php echo esc_html( $rx_custom_subtitle ); ?>
								</p>
							<?php elseif ( has_excerpt() ) : ?>
								<p class="rx-page-subtitle" itemprop="description">
									<?php echo esc_html( get_the_excerpt() ); ?>
								</p>
							<?php endif; ?>

						</div>
					<?php endif; ?>

					<?php if ( ! empty( $rx_show_page_meta ) ) : ?>
						<div class="rx-page-meta" aria-label="<?php esc_attr_e( 'Page information', 'rx-theme' ); ?>">

							<span class="rx-page-meta-item rx-page-author">
								<?php esc_html_e( 'By', 'rx-theme' ); ?>
								<a href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>" rel="author">
									<?php echo esc_html( get_the_author() ); ?>
								</a>
							</span>

							<span class="rx-page-meta-item rx-page-updated">
								<?php esc_html_e( 'Updated:', 'rx-theme' ); ?>
								<time datetime="<?php echo esc_attr( get_the_modified_date( DATE_W3C ) ); ?>" itemprop="dateModified">
									<?php echo esc_html( get_the_modified_date() ); ?>
								</time>
							</span>

							<?php if ( function_exists( 'rx_reading_time' ) ) : ?>
								<span class="rx-page-meta-item rx-page-reading-time">
									<?php echo esc_html( rx_reading_time( $rx_page_id ) ); ?>
								</span>
							<?php endif; ?>

						</div>
					<?php endif; ?>

				</div>

			</header>

			<?php if ( $rx_has_thumbnail && empty( $rx_hide_featured_image ) ) : ?>
				<section class="rx-page-featured-image-section">
					<div class="rx-container">
						<figure class="rx-page-featured-image">
							<?php
							the_post_thumbnail(
								'full',
								array(
									'class'    => 'rx-page-thumbnail-img',
									'loading'  => 'eager',
									'decoding' => 'async',
									'itemprop' => 'primaryImageOfPage',
								)
							);
							?>

							<?php
							$rx_thumbnail_caption = wp_get_attachment_caption( get_post_thumbnail_id() );

							if ( ! empty( $rx_thumbnail_caption ) ) :
								?>
								<figcaption class="rx-page-featured-caption">
									<?php echo wp_kses_post( $rx_thumbnail_caption ); ?>
								</figcaption>
							<?php endif; ?>
						</figure>
					</div>
				</section>
			<?php endif; ?>

			<div class="<?php echo esc_attr( $rx_container_class ); ?>">

				<section class="rx-page-content-area">

					<?php
					/**
					 * RX before page content hook.
					 */
					do_action( 'rx_before_page_content', $rx_page_id );
					?>

					<?php if ( function_exists( 'rx_table_of_contents' ) ) : ?>
						<aside class="rx-page-toc" aria-label="<?php esc_attr_e( 'Table of contents', 'rx-theme' ); ?>">
							<?php rx_table_of_contents(); ?>
						</aside>
					<?php endif; ?>

					<div class="rx-page-content entry-content" itemprop="mainContentOfPage">

						<?php
						the_content();

						wp_link_pages(
							array(
								'before'      => '<nav class="rx-page-links" aria-label="' . esc_attr__( 'Page navigation', 'rx-theme' ) . '"><span class="rx-page-links-title">' . esc_html__( 'Pages:', 'rx-theme' ) . '</span>',
								'after'       => '</nav>',
								'link_before' => '<span class="rx-page-link-number">',
								'link_after'  => '</span>',
							)
						);
						?>

					</div>

					<?php
					/**
					 * Admin edit link.
					 */
					edit_post_link(
						esc_html__( 'Edit this page', 'rx-theme' ),
						'<div class="rx-edit-link">',
						'</div>'
					);
					?>

					<?php
					/**
					 * Child pages section.
					 */
					$rx_child_pages = new WP_Query(
						array(
							'post_type'              => 'page',
							'post_parent'            => $rx_page_id,
							'posts_per_page'         => 12,
							'orderby'                => 'menu_order title',
							'order'                  => 'ASC',
							'no_found_rows'          => true,
							'update_post_meta_cache' => false,
							'update_post_term_cache' => false,
						)
					);

					if ( ! empty( $rx_show_child_pages ) && $rx_child_pages->have_posts() ) :
						?>
						<section class="rx-child-pages-section" aria-labelledby="rx-child-pages-title">

							<h2 id="rx-child-pages-title" class="rx-section-title">
								<?php esc_html_e( 'Explore More Pages', 'rx-theme' ); ?>
							</h2>

							<div class="rx-child-pages-grid">

								<?php while ( $rx_child_pages->have_posts() ) : ?>
									<?php $rx_child_pages->the_post(); ?>

									<article id="child-page-<?php the_ID(); ?>" <?php post_class( 'rx-child-page-card' ); ?>>

										<?php if ( has_post_thumbnail() ) : ?>
											<a class="rx-child-page-thumb-link" href="<?php the_permalink(); ?>" aria-label="<?php the_title_attribute(); ?>">
												<?php
												the_post_thumbnail(
													'medium_large',
													array(
														'class'    => 'rx-child-page-thumb',
														'loading'  => 'lazy',
														'decoding' => 'async',
													)
												);
												?>
											</a>
										<?php endif; ?>

										<div class="rx-child-page-card-body">
											<h3 class="rx-child-page-title">
												<a href="<?php the_permalink(); ?>">
													<?php the_title(); ?>
												</a>
											</h3>

											<?php if ( has_excerpt() ) : ?>
												<p class="rx-child-page-excerpt">
													<?php echo esc_html( wp_trim_words( get_the_excerpt(), 24, '...' ) ); ?>
												</p>
											<?php endif; ?>

											<a class="rx-read-more-link" href="<?php the_permalink(); ?>">
												<?php esc_html_e( 'Read more', 'rx-theme' ); ?>
												<span aria-hidden="true"></span>
											</a>
										</div>

									</article>

								<?php endwhile; ?>

							</div>

						</section>
						<?php
						wp_reset_postdata();
					endif;
					?>

					<?php
					/**
					 * Related content section.
					 *
					 * If your helper function exists, it will run.
					 * Otherwise a fallback section will show recent pages.
					 */
					if ( ! empty( $rx_show_related ) ) :
						?>

						<section class="rx-related-pages-section" aria-labelledby="rx-related-pages-title">

							<h2 id="rx-related-pages-title" class="rx-section-title">
								<?php esc_html_e( 'Related Information', 'rx-theme' ); ?>
							</h2>

							<?php if ( function_exists( 'rx_related_posts' ) ) : ?>

								<?php rx_related_posts( $rx_page_id ); ?>

							<?php else : ?>

								<?php
								$rx_related_pages = new WP_Query(
									array(
										'post_type'              => 'page',
										'post__not_in'           => array( $rx_page_id ),
										'posts_per_page'         => 6,
										'orderby'                => 'modified',
										'order'                  => 'DESC',
										'no_found_rows'          => true,
										'update_post_meta_cache' => false,
										'update_post_term_cache' => false,
									)
								);

								if ( $rx_related_pages->have_posts() ) :
									?>
									<div class="rx-related-pages-grid">

										<?php while ( $rx_related_pages->have_posts() ) : ?>
											<?php $rx_related_pages->the_post(); ?>

											<article id="related-page-<?php the_ID(); ?>" <?php post_class( 'rx-related-page-card' ); ?>>

												<h3 class="rx-related-page-title">
													<a href="<?php the_permalink(); ?>">
														<?php the_title(); ?>
													</a>
												</h3>

												<?php if ( has_excerpt() ) : ?>
													<p class="rx-related-page-excerpt">
														<?php echo esc_html( wp_trim_words( get_the_excerpt(), 22, '...' ) ); ?>
													</p>
												<?php endif; ?>

											</article>

										<?php endwhile; ?>

									</div>
									<?php
									wp_reset_postdata();
								endif;
								?>

							<?php endif; ?>

						</section>

					<?php endif; ?>

					<?php
					/**
					 * Previous / next navigation.
					 *
					 * Pages usually do not use category navigation like posts,
					 * so this uses parent/child/sibling page logic when available.
					 */
					$rx_sibling_args = array(
						'post_type'      => 'page',
						'post_parent'    => wp_get_post_parent_id( $rx_page_id ),
						'posts_per_page' => -1,
						'orderby'        => 'menu_order title',
						'order'          => 'ASC',
						'fields'         => 'ids',
					);

					$rx_sibling_pages = get_posts( $rx_sibling_args );
					$rx_current_index = array_search( $rx_page_id, $rx_sibling_pages, true );

					$rx_prev_page_id = false;
					$rx_next_page_id = false;

					if ( false !== $rx_current_index ) {
						$rx_prev_page_id = isset( $rx_sibling_pages[ $rx_current_index - 1 ] ) ? $rx_sibling_pages[ $rx_current_index - 1 ] : false;
						$rx_next_page_id = isset( $rx_sibling_pages[ $rx_current_index + 1 ] ) ? $rx_sibling_pages[ $rx_current_index + 1 ] : false;
					}

					if ( $rx_prev_page_id || $rx_next_page_id ) :
						?>
						<nav class="rx-page-navigation" aria-label="<?php esc_attr_e( 'Page navigation', 'rx-theme' ); ?>">

							<?php if ( $rx_prev_page_id ) : ?>
								<div class="rx-page-nav-item rx-page-nav-prev">
									<span class="rx-page-nav-label">
										<?php esc_html_e( 'Previous Page', 'rx-theme' ); ?>
									</span>
									<a href="<?php echo esc_url( get_permalink( $rx_prev_page_id ) ); ?>">
										<?php echo esc_html( get_the_title( $rx_prev_page_id ) ); ?>
									</a>
								</div>
							<?php endif; ?>

							<?php if ( $rx_next_page_id ) : ?>
								<div class="rx-page-nav-item rx-page-nav-next">
									<span class="rx-page-nav-label">
										<?php esc_html_e( 'Next Page', 'rx-theme' ); ?>
									</span>
									<a href="<?php echo esc_url( get_permalink( $rx_next_page_id ) ); ?>">
										<?php echo esc_html( get_the_title( $rx_next_page_id ) ); ?>
									</a>
								</div>
							<?php endif; ?>

						</nav>
					<?php endif; ?>

					<?php
					/**
					 * RX after page content hook.
					 */
					do_action( 'rx_after_page_content', $rx_page_id );
					?>

					<?php
					/**
					 * Comments section.
					 */
					if ( comments_open() || get_comments_number() ) :
						comments_template();
					endif;
					?>

				</section>

				<?php if ( $rx_show_sidebar ) : ?>
					<aside id="secondary" class="rx-page-sidebar widget-area" role="complementary" aria-label="<?php esc_attr_e( 'Page sidebar', 'rx-theme' ); ?>">
						<?php dynamic_sidebar( 'sidebar-page' ); ?>
					</aside>
				<?php endif; ?>

			</div>

			<?php
			/**
			 * JSON-LD schema fallback.
			 *
			 * If you already have a full schema system in inc/helpers/schema.php,
			 * you may remove this block or replace it with your own helper.
			 */
			if ( ! function_exists( 'rx_schema_webpage_output' ) ) :
				$rx_schema_image = $rx_has_thumbnail ? get_the_post_thumbnail_url( $rx_page_id, 'full' ) : '';
				$rx_schema_data  = array(
					'@context'     => 'https://schema.org',
					'@type'        => 'WebPage',
					'@id'          => esc_url_raw( get_permalink() ) . '#webpage',
					'url'          => esc_url_raw( get_permalink() ),
					'name'         => wp_strip_all_tags( get_the_title() ),
					'description'  => wp_strip_all_tags( get_the_excerpt() ),
					'inLanguage'   => get_bloginfo( 'language' ),
					'isPartOf'     => array(
						'@type' => 'WebSite',
						'name'  => get_bloginfo( 'name' ),
						'url'   => esc_url_raw( home_url( '/' ) ),
					),
					'datePublished' => get_the_date( DATE_W3C ),
					'dateModified'  => get_the_modified_date( DATE_W3C ),
				);

				if ( ! empty( $rx_schema_image ) ) {
					$rx_schema_data['primaryImageOfPage'] = array(
						'@type' => 'ImageObject',
						'url'   => esc_url_raw( $rx_schema_image ),
					);
				}
				?>
				<script type="application/ld+json">
					<?php echo wp_json_encode( $rx_schema_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT ); ?>
				</script>
			<?php endif; ?>

			<?php
			/**
			 * RX after page article hook.
			 */
			do_action( 'rx_after_page_article', $rx_page_id );
			?>

		</article>

	<?php endwhile; ?>

	<?php
	/**
	 * RX after page main hook.
	 */
	do_action( 'rx_after_page_main' );
	?>

</main>

<?php
/**
 * RX after page template hook.
 */
do_action( 'rx_after_page_template' );

get_footer();

Optional sidebar registration for functions.php

Because this page.php supports a page sidebar named sidebar-page, add this in your sidebar registration file or functions.php if you have not already added it:

function rx_register_page_sidebar() {
	register_sidebar(
		array(
			'name'          => esc_html__( 'Page Sidebar', 'rx-theme' ),
			'id'            => 'sidebar-page',
			'description'   => esc_html__( 'Widgets added here will appear on static pages.', 'rx-theme' ),
			'before_widget' => '<section id="%1$s" class="widget rx-widget %2$s">',
			'after_widget'  => '</section>',
			'before_title'  => '<h2 class="widget-title rx-widget-title">',
			'after_title'   => '</h2>',
		)
	);
}
add_action( 'widgets_init', 'rx_register_page_sidebar' );

Optional CSS for style.css

.rx-container {
	width: min(1200px, calc(100% - 32px));
	margin-inline: auto;
}

.rx-layout {
	display: grid;
	gap: 32px;
}

.rx-layout-sidebar {
	grid-template-columns: minmax(0, 1fr) 320px;
	align-items: start;
}

.rx-layout-full {
	grid-template-columns: minmax(0, 1fr);
}

.rx-page-header {
	padding: 48px 0 24px;
}

.rx-page-title {
	margin: 0;
	font-size: clamp(2rem, 4vw, 4rem);
	line-height: 1.1;
}

.rx-page-subtitle {
	margin-top: 16px;
	max-width: 760px;
	font-size: 1.15rem;
	line-height: 1.7;
	opacity: 0.85;
}

.rx-page-parent-label {
	margin-bottom: 10px;
	font-weight: 600;
}

.rx-page-meta {
	display: flex;
	flex-wrap: wrap;
	gap: 12px 20px;
	margin-top: 18px;
	font-size: 0.95rem;
	opacity: 0.85;
}

.rx-page-featured-image-section {
	margin-bottom: 32px;
}

.rx-page-featured-image {
	margin: 0;
}

.rx-page-thumbnail-img {
	width: 100%;
	height: auto;
	border-radius: 18px;
	display: block;
}

.rx-page-featured-caption {
	margin-top: 10px;
	font-size: 0.9rem;
	text-align: center;
	opacity: 0.75;
}

.rx-page-content-area {
	min-width: 0;
}

.rx-page-content {
	font-size: 1.05rem;
	line-height: 1.8;
}

.rx-page-content img {
	max-width: 100%;
	height: auto;
}

.rx-page-content h2,
.rx-page-content h3,
.rx-page-content h4 {
	scroll-margin-top: 100px;
}

.rx-page-toc {
	padding: 20px;
	margin-bottom: 28px;
	border: 1px solid rgba(0,0,0,0.08);
	border-radius: 16px;
	background: rgba(0,0,0,0.02);
}

.rx-page-links {
	margin-top: 28px;
	padding-top: 20px;
	border-top: 1px solid rgba(0,0,0,0.08);
}

.rx-page-link-number {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 34px;
	height: 34px;
	margin: 4px;
	border-radius: 999px;
	border: 1px solid rgba(0,0,0,0.15);
}

.rx-child-pages-section,
.rx-related-pages-section {
	margin-top: 48px;
}

.rx-section-title {
	margin-bottom: 20px;
	font-size: 1.7rem;
}

.rx-child-pages-grid,
.rx-related-pages-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 24px;
}

.rx-child-page-card,
.rx-related-page-card {
	padding: 20px;
	border: 1px solid rgba(0,0,0,0.08);
	border-radius: 18px;
	background: #fff;
}

.rx-child-page-thumb {
	width: 100%;
	height: auto;
	border-radius: 14px;
	margin-bottom: 14px;
}

.rx-child-page-title,
.rx-related-page-title {
	margin-top: 0;
	font-size: 1.2rem;
	line-height: 1.35;
}

.rx-read-more-link {
	display: inline-flex;
	gap: 6px;
	align-items: center;
	font-weight: 700;
	text-decoration: none;
}

.rx-page-navigation {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 20px;
	margin-top: 48px;
	padding-top: 28px;
	border-top: 1px solid rgba(0,0,0,0.08);
}

.rx-page-nav-item {
	padding: 18px;
	border-radius: 16px;
	border: 1px solid rgba(0,0,0,0.08);
}

.rx-page-nav-next {
	text-align: right;
}

.rx-page-nav-label {
	display: block;
	margin-bottom: 6px;
	font-size: 0.85rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	opacity: 0.7;
}

.rx-edit-link {
	margin-top: 32px;
}

.rx-page-sidebar {
	padding: 24px;
	border-radius: 18px;
	border: 1px solid rgba(0,0,0,0.08);
	background: rgba(0,0,0,0.02);
}

@media (max-width: 900px) {
	.rx-layout-sidebar {
		grid-template-columns: 1fr;
	}

	.rx-child-pages-grid,
	.rx-related-pages-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 600px) {
	.rx-child-pages-grid,
	.rx-related-pages-grid,
	.rx-page-navigation {
		grid-template-columns: 1fr;
	}

	.rx-page-nav-next {
		text-align: left;
	}
}

Important custom field controls

This page.php supports these optional custom fields:

rx_hide_page_title
rx_hide_featured_image
rx_show_page_meta
rx_show_child_pages
rx_show_related_content
rx_page_subtitle

Example values:

rx_show_child_pages = 1
rx_show_related_content = 1
rx_show_page_meta = 1
rx_hide_page_title = 1
rx_hide_featured_image = 1
rx_page_subtitle = Your custom subtitle here

This is a strong advanced page.php base for your RX Theme. It will work now, and later you can connect it with your helper files like:

inc/helpers/breadcrumbs.php
inc/helpers/reading-time.php
inc/helpers/related-posts.php
inc/helpers/schema.php
inc/helpers/table-of-contents.php

Leave a Reply

Your email address will not be published. Required fields are marked *