How to Build a Native AMP WordPress Site for Faster Pages

After building several AMP sites for clients, I discovered it’s surprisingly straightforward to create a fully custom theme that is AMP-compatible.

In about an hour I converted this site to native AMP. You can check the AMP validator to confirm these posts are valid AMP pages.

Below I’ll cover the essentials of setting up an AMP site, highlight common pitfalls I encountered, and share helper functions that simplify AMP development.

Quick Links

  1. What is AMP?
  2. AMP WordPress plugin
  3. Three AMP template modes
  4. Fixing validation issues
  5. Plugins without JavaScript
  6. Navigation without JavaScript
  7. Google Analytics with AMP
  8. AMP helper functions

What is AMP?

AMP is Google’s framework designed to make web pages load much faster. It relies on a restricted subset of HTML, limits page CSS to 50 KB, and disallows custom JavaScript in the page markup.

Services like Cloudflare can preserve your site’s real URL for AMP cached pages, avoiding the Google cached URL display.

Why use AMP?

  • AMP enforces performance best practices, which helps SEO since page speed is a ranking factor.
  • Google can pre-render AMP pages, making perceived load times feel nearly instantaneous for users.
  • AMP pages are eligible for special placements such as the Top Stories carousel in search results.
  • The AMP WordPress plugin includes CSS tree shaking to include only the CSS required for each page, often dramatically reducing stylesheet size.

AMP WordPress Plugin

The official AMP plugin for WordPress handles most of the heavy lifting. It converts your site’s output to AMP HTML for AMP endpoints, so in many cases you can continue to author templates in standard HTML and rely on the plugin to produce AMP-compliant markup.

The plugin also provides CSS tree shaking: it analyzes which styles are actually used on a page and builds a minimal page-specific stylesheet. When viewing source on an AMP page, the plugin includes a comment showing a breakdown of included CSS and how much of your total stylesheet that represents.

The admin bar gets a “Validate AMP” item so you can easily check whether the current page is AMP-valid or not, and the backend shows an “Error Index” listing pages with issues and pointing toward the plugin or theme causing them.

Some plugins historically shipped JavaScript that broke AMP validation, though many have added AMP-compatible options or updates to remove or replace problematic scripts.

Three AMP template modes

The AMP plugin offers three template modes:

Native serves only AMP pages to all visitors. Your canonical URLs are AMP, and there are no separate non-AMP pages. Native mode requires the most work because every page must meet AMP restrictions, but it results in a streamlined, fast site. This guide focuses on Native mode.

Transitional keeps your canonical URLs as non-AMP while creating paired AMP versions. Visitors reach the AMP variant via an added query string (for example, ?amp). This mode is useful if you want AMP pages that resemble your site while maintaining a full non-AMP experience as canonical.

Reader provides the simplest option: it serves a basic AMP theme (provided by the plugin) as the paired AMP version while your canonical pages remain non-AMP. It’s the quickest path to AMP for content-heavy sites.

Fixing Validation Issues

After switching to Native mode, the validator will flag pages that include disallowed JavaScript or unsupported markup from your theme or plugins. The typical workflow I follow is:

  • Audit plugins and identify ones that inject JavaScript or other non-AMP elements.
  • Review theme JavaScript to determine which features can be removed, which should be rebuilt using AMP components, and which pages can remain non-AMP (invalidated) if acceptable.
  • Recreate interactive features using AMP components where necessary (for example, menus, modals, and forms).

Plugins without JavaScript

Installing or updating plugins often triggers AMP re-validation. When a plugin adds JavaScript that breaks AMP, you have several choices:

Reject validation error, leave page invalidated

If the issue affects only a small set of pages, it may be reasonable to reject the automatic sanitization for those pages and leave them invalidated. The AMP plugin can sanitize pages by stripping out problematic scripts, but you can opt to reject that sanitization. The page will no longer be an AMP endpoint (the amp attribute is removed), yet it can still benefit from other AMP optimizations already applied. For low-impact pages—like a contact form page—this trade-off is often acceptable.

If the feature appears site-wide (for example, a newsletter signup included in every post), you should replace or update it to an AMP-compatible alternative.

Disable JavaScript in the plugin

Some plugins provide filters to disable their JavaScript. Before turning off a plugin’s scripts, review what functionality you’ll lose so you can assess the impact.

For example, Shared Counts uses JavaScript for popup sizing, an email-share popup, and social tracking events. If you don’t need those features, you can disable the JS via a filter:

add_filter( 'shared_counts_load_js', '__return_false' );

Other plugins offer similar filters to prevent enqueuing jQuery or other scripts. In your theme you can conditionally dequeue scripts only on AMP pages using a helper conditional so the JavaScript remains active for non-AMP visitors.

function ea_amp_remove_scripts() {
	if ( ! ea_is_amp() ) {
		return;
	}
	wp_dequeue_script( 'page-links-to' );
}
add_action( 'wp_enqueue_scripts', 'ea_amp_remove_scripts', 20 );

Build the functionality without a plugin

When JavaScript is essential to a plugin’s feature set, you may need to reimplement that functionality using AMP components. AMP provides many components—amp-lightbox, amp-form, amp-list, amp-bind, amp-recaptcha-input, and more—that let you recreate interactive features without custom JavaScript. In many cases, leveraging these components is cleaner and easier than maintaining custom JS, since they’re designed for performance and compatibility.

Navigation without JavaScript

Common theme navigation toggles that rely on JavaScript can be rebuilt with AMP using amp-bind (AMP.setState). The pattern is:

  • Create a state variable such as mobileMenuActive.
  • Bind classes to that state using [class] attributes so classes change when the state toggles.
  • Toggle the state with on=”tap:AMP.setState({ mobileMenuActive: !mobileMenuActive })”.

Example button and nav bindings:



To simplify this pattern, I built helper functions that generate the [class] binding and on=”tap” attribute from PHP, making templates cleaner and easier to maintain. Submenu toggles follow the same approach but use unique state variables per submenu so you can control them independently.

For accessibility and resilience, it’s wise to provide a JavaScript fallback for navigation that loads only when AMP is not active. This preserves the original behavior if the AMP plugin is disabled.

The Genesis framework includes built-in support for responsive menus that can reduce the amount of manual AMP work required, registering necessary scripts and menu classes for you.

Google Analytics with AMP

Because AMP forbids custom JavaScript, you can’t use the traditional Google Analytics snippet. Instead AMP provides amp-analytics, an AMP-specific component that accepts a JSON configuration for analytics vendors and event triggers.

The AMP plugin exposes an Analytics settings screen where you can configure amp-analytics. Alternatively, some analytics plugins and services provide AMP add-ons that automatically generate the proper configuration. These solutions often include extra features like session unification when visitors move between AMP and non-AMP pages.

AMP Helper Functions

To keep AMP-specific template code tidy, I created a set of helper functions included in /inc/amp.php in my starter themes. They include:

  • ea_is_amp() — a conditional to detect AMP endpoints so you can conditionally dequeue assets or output AMP-specific attributes.
  • ea_amp_class( $default, $active, $state ) — outputs both a normal class attribute and an AMP [class] binding that toggles between default and active class values based on a state variable.
  • ea_amp_toggle( $state, $disable ) — returns an on=”tap:AMP.setState(…)” attribute that toggles a state variable and optionally disables other states.
  • ea_amp_nav_dropdown( $theme_location, $depth ) — generates a unique state key for submenu toggles and combines ea_amp_toggle() and ea_amp_class() to produce the attributes needed for AMP submenus.

These helpers keep template markup concise and make it easier to maintain consistent AMP behavior across the site.

In summary, building a custom AMP-compatible theme is highly achievable. With the official AMP plugin, careful auditing of plugins and theme scripts, and leveraging AMP components and small helper functions, you can deliver a fast, AMP-native experience without sacrificing the look and core functionality of your site.