Skip to main content

Overview

Web Stories provides flexible archive page options, allowing you to use the default WordPress archive, disable archives entirely, or create a custom archive page.

Archive Configuration

The archive behavior is controlled through plugin settings:
Story_Post_Type.php
public function get_has_archive() {
    $archive_page_option = $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE );
    $has_archive         = true;
    
    if ( 'disabled' === $archive_page_option ) {
        $has_archive = false;
    } elseif ( 'custom' === $archive_page_option ) {
        $custom_archive_page_id = (int) $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID );
        if ( $custom_archive_page_id && 'publish' === get_post_status( $custom_archive_page_id ) ) {
            $uri = get_page_uri( $custom_archive_page_id );
            if ( $uri ) {
                $has_archive = urldecode( $uri );
            }
        }
    }
    
    return $has_archive;
}

Archive Options

There are three archive display modes:

Standard WordPress Archive

Uses the built-in WordPress post type archive functionality.URL Structure:
https://example.com/web-stories/
Template Hierarchy:
  1. archive-web-story.php
  2. archive.php
  3. index.php
The default archive shows all published stories using your theme’s archive template.

Custom Archive Implementation

When a custom archive page is set, the plugin modifies the main query:
Story_Archive.php
public function pre_get_posts( WP_Query $query ): void {
    if ( ! \is_string( $this->story_post_type->get_has_archive() ) ) {
        return;
    }
    
    if ( $query->is_admin || ! $query->is_main_query() ) {
        return;
    }
    
    if ( ! $query->is_post_type_archive( $this->story_post_type->get_slug() ) ) {
        return;
    }
    
    $custom_archive_page_id = (int) $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID );
    
    $query->set( 'page_id', $custom_archive_page_id );
    $query->set( 'post_type', 'page' );
    $query->is_post_type_archive = false;
    $query->is_archive           = false;
    $query->is_singular          = true;
    $query->is_page              = true;
}
The query is transformed from a post type archive to a singular page query, allowing full page template support.

Archive Page Redirects

The plugin handles redirects for the default /web-stories/ URL:
Story_Archive.php
public function redirect_post_type_archive_urls( $bypass, WP_Query $query ) {
    global $wp_rewrite;
    
    if ( $bypass || ! \is_string( $this->story_post_type->get_has_archive() ) || ( ! $wp_rewrite instanceof WP_Rewrite || ! $wp_rewrite->using_permalinks() ) ) {
        return $bypass;
    }
    
    // 'pagename' is for most permalink types, 'name' is for when %postname% is used as top-level.
    if ( $this->story_post_type::REWRITE_SLUG === $query->get( 'pagename' ) || $this->story_post_type::REWRITE_SLUG === $query->get( 'name' ) ) {
        $redirect_url = get_post_type_archive_link( $this->story_post_type->get_slug() );
        
        if ( $redirect_url && wp_safe_redirect( $redirect_url, 301 ) ) {
            exit;
        }
    }
    
    return $bypass;
}

Archive Page States

Custom archive pages show a special indicator in the WordPress admin:
Story_Archive.php
public function filter_display_post_states( $post_states, ?WP_Post $post ) {
    if ( ! \is_array( $post_states ) || ! $post ) {
        return $post_states;
    }
    
    if ( ! \is_string( $this->story_post_type->get_has_archive() ) ) {
        return $post_states;
    }
    
    $custom_archive_page_id = (int) $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID );
    
    if ( $post->ID === $custom_archive_page_id ) {
        $post_states['web_stories_archive_page'] = __( 'Web Stories Archive Page', 'web-stories' );
    }
    
    return $post_states;
}
This adds a “Web Stories Archive Page” label in the pages list.

Archive Page Deletion

When a custom archive page is trashed or deleted, settings reset automatically:
Story_Archive.php
public function on_remove_archive_page( int $postid ): void {
    if ( 'page' !== get_post_type( $postid ) ) {
        return;
    }
    
    $custom_archive_page_id = (int) $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID );
    
    if ( $custom_archive_page_id !== $postid ) {
        return;
    }
    
    $this->settings->update_setting( $this->settings::SETTING_NAME_ARCHIVE, 'default' );
    $this->settings->update_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID, 0 );
}

Rewrite Rules

Changing archive settings triggers a rewrite flush:
Story_Archive.php
public function update_archive_setting(): void {
    $this->story_post_type->unregister_post_type();
    $this->story_post_type->register_post_type();
    
    if ( ! \defined( '\WPCOM_IS_VIP_ENV' ) || false === \WPCOM_IS_VIP_ENV ) {
        flush_rewrite_rules( false );
    }
}
Rewrite rules are not flushed on VIP environments. Use the WP-CLI command wp rewrite flush instead.

Creating a Custom Archive Template

Create a custom archive template in your theme:
1

Create Template File

Create archive-web-story.php in your theme directory.
2

Add Template Header

archive-web-story.php
<?php
/**
 * Template Name: Web Stories Archive
 * Description: Custom archive for Web Stories
 */

get_header();
?>
3

Query Stories

<div class="web-stories-archive">
    <h1><?php post_type_archive_title(); ?></h1>
    
    <?php
    $args = [
        'post_type'      => 'web-story',
        'posts_per_page' => 12,
        'paged'          => get_query_var( 'paged' ) ?: 1,
    ];
    
    $stories_query = new WP_Query( $args );
    
    if ( $stories_query->have_posts() ) :
        while ( $stories_query->have_posts() ) :
            $stories_query->the_post();
            // Display story
        endwhile;
        
        // Pagination
        the_posts_pagination();
    endif;
    
    wp_reset_postdata();
    ?>
</div>
4

Add Footer

<?php get_footer(); ?>

Using Story_Query in Templates

Use the Story_Query class for advanced story displays:
$story_attrs = [
    'view_type'         => 'grid',
    'number_of_columns' => 3,
    'show_title'        => true,
    'show_author'       => true,
    'show_date'         => true,
];

$story_args = [
    'posts_per_page' => 12,
    'orderby'        => 'post_date',
    'order'          => 'DESC',
];

$story_query = new Google\Web_Stories\Story_Query( $story_attrs, $story_args );
echo $story_query->render();
Get the archive link programmatically:
$archive_url = get_post_type_archive_link( 'web-story' );

if ( $archive_url ) {
    echo '<a href="' . esc_url( $archive_url ) . '">View All Stories</a>';
}
The function returns false if archives are disabled.

Pagination

Implement pagination in custom archive templates:
the_posts_pagination([
    'mid_size'  => 2,
    'prev_text' => __( 'Previous Stories', 'textdomain' ),
    'next_text' => __( 'More Stories', 'textdomain' ),
]);

Archive Page Hooks

The archive system uses WordPress hooks:
// Fires when archive setting is added
add_action( 'add_option_web_stories_archive', 'my_archive_callback' );

// Fires when archive setting is updated  
add_action( 'update_option_web_stories_archive', 'my_archive_callback' );

// Fires when archive page ID is updated
add_action( 'update_option_web_stories_archive_page_id', 'my_archive_callback' );

SEO Considerations

Customize the archive page title using SEO plugins or the document_title_parts filter:
add_filter( 'document_title_parts', function( $title ) {
    if ( is_post_type_archive( 'web-story' ) ) {
        $title['title'] = 'Our Web Stories';
    }
    return $title;
});
Add a custom description to your archive template:
if ( is_post_type_archive( 'web-story' ) ) {
    echo '<meta name="description" content="Browse our collection of engaging Web Stories">';
}
The plugin automatically adds structured data for stories. You can extend it for the archive page using the web_stories_schema filter.

Best Practices

Performance

  • Limit stories per page (12-20 recommended)
  • Use lazy loading for images
  • Enable caching for archive pages
  • Use no_found_rows when pagination isn’t needed

User Experience

  • Provide filtering/sorting options
  • Show clear pagination
  • Include search functionality
  • Display story metadata (author, date)

Content Strategy

  • Feature latest/popular stories
  • Organize by categories
  • Add featured story sections
  • Include related content

Mobile Design

  • Use responsive grid layouts
  • Optimize touch targets
  • Test swipe gestures
  • Ensure fast load times

Next Steps

WordPress Integration

Learn about the custom post type

Gutenberg Blocks

Embed stories with blocks

Build docs developers (and LLMs) love