---
title: Ensure content parity between mobile and desktop
description: >-
  Mobile and desktop versions must contain the same core content. Google
  primarily uses the mobile version for indexing and ranking, so missing or
  hidden content on mobile results in lower search rankings.
source_url:
  html: >-
    https://shopify.dev/docs/storefronts/themes/best-practices/performance/ensure-content-parity-mobile-desktop
  md: >-
    https://shopify.dev/docs/storefronts/themes/best-practices/performance/ensure-content-parity-mobile-desktop.md
api_name: liquid
---

# Ensure content parity between mobile and desktop

Mobile and desktop versions must contain the same core content for Google's mobile-first indexing. Missing or hidden content on mobile can result in lower search rankings, as Google primarily uses the mobile version for indexing and ranking.

***

## Why

Google switched to mobile-first indexing, so the mobile version of your site is the primary version used for ranking. If important content exists only on desktop, Google might not see it, which results in lower rankings for related queries.

Content parity issues create specific problems:

* **Ranking drops**: missing mobile content leads to lower rankings for those topics.
* **Indexing gaps**: content hidden on mobile might not be indexed at all.
* **User experience inconsistency**: users expect the same information regardless of device.
* **Conversion impact**: missing CTAs or product details on mobile reduce conversions.
* **SEO signal loss**: structured data, links, and text missing on mobile aren't counted.

Common content parity mistakes:

* Hiding content in collapsed accordions or tabs.
* Removing sections entirely on mobile.
* Using different URLs for mobile and desktop.
* Lazy loading content that never renders.
* Hiding text behind **Read more** links.
* Removing product descriptions or specifications on mobile.

Google's John Mueller confirmed that content hidden in tabs or accordions on mobile is given less weight than visible content. Research shows that 30 to 40 percent of ecommerce sites have significant content differences between mobile and desktop.

***

## How

### Audit content differences

Identify content gaps:

1. Compare mobile and desktop versions side by side.
2. Check for missing sections on mobile.
3. Verify that all text content is present.
4. Confirm that structured data is identical.
5. Check that internal links are present.
6. Verify that images and media are included.

Use Google's tools: use Google Search Console **URL Inspection** to compare rendered HTML for both mobile and desktop versions.

### Make hidden content visible

Anti-pattern: content hidden in a collapsed accordion:

```liquid
<!-- Content hidden by default on mobile -->
<div class="product-description mobile-hidden">
  <button onclick="toggleDescription()">Read More</button>
  <div id="description" style="display: none;">
    {{ product.description }}
  </div>
</div>
```

Recommended: content visible, optionally collapsible:

```liquid
<!-- Content visible by default, can be collapsed -->
<div class="product-description">
  <div class="description-content">
    {{ product.description }}
  </div>
  {% if product.description.size > 500 %}
    <button class="description-toggle" onclick="toggleDescription()">
      Show Less
    </button>
  {% endif %}
</div>


<style>
  .description-content {
    /* Content is visible and indexable */
    max-height: none;
    overflow: visible;
  }


  /* Optional: add "Show less" functionality */
  .description-content.collapsed {
    max-height: 300px;
    overflow: hidden;
  }
</style>
```

### Use proper accordion implementation

Accessible, indexable accordion:

```liquid
{%- comment -%}
  Content is in the DOM and indexable, but visually collapsed.
{%- endcomment -%}


<div class="accordion">
  <h3>
    <button class="accordion-header" aria-expanded="true" aria-controls="panel1">
      Product details
      <span class="accordion-icon">−</span>
    </button>
  </h3>
  <div id="panel1" class="accordion-panel">
    {{ product.description }}
  </div>


  <h3>
    <button class="accordion-header" aria-expanded="false" aria-controls="panel2">
      Shipping information
      <span class="accordion-icon">+</span>
    </button>
  </h3>
  <div id="panel2" class="accordion-panel" hidden>
    {{ section.settings.shipping_info }}
  </div>
</div>


<style>
  .accordion-panel {
    /* Content is in the DOM, just visually hidden when collapsed */
    overflow: hidden;
    transition: max-height 0.3s;
  }


  .accordion-panel[hidden] {
    display: block; /* Keep in the DOM for indexing */
    max-height: 0;
    visibility: hidden;
  }


  .accordion-header[aria-expanded="true"] + .accordion-panel {
    max-height: 2000px;
    visibility: visible;
  }
</style>


<script>
  document.querySelectorAll('.accordion-header').forEach(button => {
    button.addEventListener('click', function() {
      const expanded = this.getAttribute('aria-expanded') === 'true';
      this.setAttribute('aria-expanded', !expanded);


      const panel = document.getElementById(this.getAttribute('aria-controls'));
      panel.hidden = expanded;


      this.querySelector('.accordion-icon').textContent = expanded ? '+' : '−';
    });
  });
</script>
```

### Ensure tab content is indexable

Proper tab implementation:

```liquid
{%- comment -%}
  All tab content is in the DOM, only display changes.
{%- endcomment -%}


<div class="tabs">
  <div class="tab-buttons" role="tablist">
    <button class="tab-button active" role="tab" aria-selected="true" aria-controls="tab1">
      Description
    </button>
    <button class="tab-button" role="tab" aria-selected="false" aria-controls="tab2">
      Specifications
    </button>
    <button class="tab-button" role="tab" aria-selected="false" aria-controls="tab3">
      Reviews
    </button>
  </div>


  <div class="tab-panels">
    <div id="tab1" class="tab-panel active" role="tabpanel">
      {{ product.description }}
    </div>


    <div id="tab2" class="tab-panel" role="tabpanel">
      {% if product.metafields.custom.specs %}
        {{ product.metafields.custom.specs.value }}
      {% endif %}
    </div>


    <div id="tab3" class="tab-panel" role="tabpanel">
      {% if product.metafields.custom.reviews %}
        {{ product.metafields.custom.reviews.value }}
      {% endif %}
    </div>
  </div>
</div>


<style>
  .tab-panel {
    /* All panels are in the DOM; only visibility changes */
    display: block;
    opacity: 0;
    height: 0;
    overflow: hidden;
    transition: opacity 0.3s;
  }


  .tab-panel.active {
    opacity: 1;
    height: auto;
    overflow: visible;
  }
</style>
```

### Avoid `display: none` for important content

Anti-pattern: using `display: none`:

```css
/* Content removed from rendering is not reliably indexed */
@media (max-width: 767px) {
  .product-specs {
    display: none; /* Google might not index this */
  }
}
```

Recommended: use visibility or positioning:

```css
/* Content is in the DOM, just not visible */
@media (max-width: 767px) {
  .product-specs {
    /* Option 1: collapse but keep in the DOM */
    max-height: 0;
    overflow: hidden;
    opacity: 0;
  }


}
```

Don't move content off-screen with `position: absolute; left: -10000px` to satisfy content parity. Content that no buyer can see is weighted less than visible content, so an off-screen copy doesn't restore parity. It only hides the gap. Render the content where buyers can reach it, such as in an expandable accordion that keeps the text in the DOM.

### Maintain the same structured data

Make sure schema markup is identical on mobile and desktop:

```liquid
{%- comment -%}
  Use the same structured data on mobile and desktop.
{%- endcomment -%}


<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "Product",
  "name": {{ product.title | json }},
  "image": {{ product.featured_image | image_url: width: 1200 | json }},
  "description": {{ product.description | strip_html | json }},
  "sku": {{ product.selected_or_first_available_variant.sku | json }},
  "brand": {
    "@type": "Brand",
    "name": {{ product.vendor | json }}
  },
  "offers": {
    "@type": "Offer",
    "url": "{{ shop.url }}{{ product.url }}",
    "priceCurrency": "{{ cart.currency.iso_code }}",
    "price": "{{ product.selected_or_first_available_variant.price | divided_by: 100.0 }}",
    "availability": "{% if product.available %}https://schema.org/InStock{% else %}https://schema.org/OutOfStock{% endif %}"
  }
}
</script>
```

### Keep internal links consistent

Same navigation and links on mobile:

```liquid
{%- comment -%}
  The mobile nav contains all desktop links.
{%- endcomment -%}


<nav class="site-nav">
  <!-- Mobile menu -->
  <div class="mobile-nav">
    <button class="menu-toggle">Menu</button>
    <ul class="mobile-menu">
      {% for link in linklists.main-menu.links %}
        <li>
          <a href="{{ link.url }}">{{ link.title }}</a>
          {% if link.links.size > 0 %}
            <ul class="submenu">
              {% for sublink in link.links %}
                <li><a href="{{ sublink.url }}">{{ sublink.title }}</a></li>
              {% endfor %}
            </ul>
          {% endif %}
        </li>
      {% endfor %}
    </ul>
  </div>


  <!-- Desktop nav, same links -->
  <ul class="desktop-nav">
    {% for link in linklists.main-menu.links %}
      <li>
        <a href="{{ link.url }}">{{ link.title }}</a>
        {% if link.links.size > 0 %}
          <ul class="dropdown">
            {% for sublink in link.links %}
              <li><a href="{{ sublink.url }}">{{ sublink.title }}</a></li>
            {% endfor %}
          </ul>
        {% endif %}
      </li>
    {% endfor %}
  </ul>
</nav>
```

### Responsive images with the same content

Serve appropriately sized images, but the same content:

```liquid
{%- comment -%}
  Serve appropriately sized images, but the same content.
{%- endcomment -%}


<img
  src="{{ product.featured_image | image_url: width: 800 }}"
  srcset="
    {{ product.featured_image | image_url: width: 400 }} 400w,
    {{ product.featured_image | image_url: width: 800 }} 800w,
    {{ product.featured_image | image_url: width: 1200 }} 1200w,
    {{ product.featured_image | image_url: width: 1600 }} 1600w
  "
  sizes="(max-width: 768px) 100vw, 50vw"
  alt="{{ product.title | escape }}"
>
```

***

## Examples

### Product description with content parity

```liquid
<div class="product-info">
  <h1>{{ product.title }}</h1>


  <div class="product-price">
    {{ product.price | money }}
  </div>


  <!-- Full description visible on both mobile and desktop -->
  <div class="product-description">
    <h2>Description</h2>
    <div class="description-content">
      {{ product.description }}
    </div>
  </div>


  <!-- Specifications visible on both -->
  {% if product.metafields.custom.specs %}
    <div class="product-specs">
      <h2>Specifications</h2>
      <div class="specs-content">
        {{ product.metafields.custom.specs.value }}
      </div>
    </div>
  {% endif %}
</div>


<style>
  /* Content visible on all screen sizes */
  .product-description,
  .product-specs {
    margin: 32px 0;
  }


  /* Adjust layout, not content visibility */
  @media (max-width: 767px) {
    .product-info {
      padding: 20px;
    }
  }


  @media (min-width: 768px) {
    .product-info {
      max-width: 1200px;
      margin: 0 auto;
      padding: 40px;
    }
  }
</style>
```

### Collection page with the same products

```liquid
{%- comment -%}
  Show the same products on mobile and desktop; adjust the layout only.
{%- endcomment -%}


<div class="collection-grid">
  {% for product in collection.products %}
    <div class="product-card">
      <a href="{{ product.url }}">
        <img
          src="{{ product.featured_image | image_url: width: 600 }}"
          srcset="
            {{ product.featured_image | image_url: width: 300 }} 300w,
            {{ product.featured_image | image_url: width: 600 }} 600w,
            {{ product.featured_image | image_url: width: 900 }} 900w
          "
          sizes="(max-width: 767px) 100vw, (max-width: 1023px) 50vw, 33vw"
          alt="{{ product.title | escape }}"
        >
        <h3>{{ product.title }}</h3>
        <p>{{ product.price | money }}</p>
      </a>
    </div>
  {% endfor %}
</div>


<style>
  .collection-grid {
    display: grid;
    gap: 20px;
    padding: 20px;
  }


  /* Mobile: 1 column */
  @media (max-width: 767px) {
    .collection-grid {
      grid-template-columns: 1fr;
    }
  }


  /* Tablet: 2 columns */
  @media (min-width: 768px) and (max-width: 1023px) {
    .collection-grid {
      grid-template-columns: repeat(2, 1fr);
    }
  }


  /* Desktop: 3 columns */
  @media (min-width: 1024px) {
    .collection-grid {
      grid-template-columns: repeat(3, 1fr);
    }
  }
</style>
```

***

## Testing

1. **Visual comparison**: view the site on mobile and desktop side by side. Check for missing sections, verify that all text is present, and confirm that images are included.

2. **Google Search Console**: use the **URL Inspection** tool to compare mobile and desktop rendered HTML. Check for content differences and verify that structured data is identical.

3. **Device emulation**: use [Chrome DevTools device mode](https://developer.chrome.com/docs/devtools/device-mode) to render the page at a mobile viewport. Compare the DOM against the desktop render and check for content that's present in one and missing from the other.

4. **Crawl comparison**: use Screaming Frog or a similar tool with mobile and desktop user agents. Compare word counts and check for missing links.

5. **Structured data testing**: use the Rich Results Test for both mobile and desktop. Verify that schema markup is identical.

6. **Manual testing**: test that accordions and tabs expand properly. Verify that all content is accessible and all internal links work.

***

## References

* [Google: mobile-first indexing](https://developers.google.com/search/mobile-sites/mobile-first-indexing)
* [Google: content parity guidance](https://developers.google.com/search/blog/2016/11/mobile-first-indexing)
* [Google Search Console: URL Inspection](https://support.google.com/webmasters/answer/9012289)

***
