Every Shopify theme ships with a set of pre-built sections — hero banners, featured collections, testimonials, image-with-text. These are designed to cover the most common use cases for most stores.
Your store is not most stores.
When you need a section that your theme doesn't provide — a product comparison table, a before-and-after slider, a multi-step quiz that recommends products, a countdown timer tied to real inventory — you have two options: install an app that adds it (with the performance and cost implications that brings), or have a developer build it as a native Shopify section.
Custom sections built by a developer load faster, look exactly like your brand, and give your content team full editing control in the Shopify theme editor without touching code. Here's what that looks like in practice.
How Shopify Sections Actually Work
A Shopify section is a Liquid template file in your theme's sections/ directory. It has two parts:
The template — HTML and Liquid that renders the section's content on the page.
The schema — A JSON block at the bottom of the file that defines what settings appear in the theme editor. Settings can be text fields, dropdowns, image selectors, color pickers, product/collection references, checkboxes, and more.
{% comment %} sections/custom-hero.liquid {% endcomment %}
<section
class="custom-hero"
style="background-color: {{ section.settings.bg_color }};"
>
<div class="hero-content">
<h1>{{ section.settings.heading }}</h1>
<p>{{ section.settings.subheading }}</p>
<a href="{{ section.settings.button_url }}" class="btn">
{{ section.settings.button_text }}
</a>
</div>
{% if section.settings.hero_image %}
<img
src="{{ section.settings.hero_image | image_url: width: 1200 }}"
alt="{{ section.settings.hero_image.alt | escape }}"
width="1200"
loading="eager"
>
{% endif %}
</section>
{% schema %}
{
"name": "Custom Hero",
"settings": [
{ "type": "text", "id": "heading", "label": "Heading" },
{ "type": "textarea", "id": "subheading", "label": "Subheading" },
{ "type": "url", "id": "button_url", "label": "Button URL" },
{ "type": "text", "id": "button_text", "label": "Button text", "default": "Shop Now" },
{ "type": "image_picker", "id": "hero_image", "label": "Hero image" },
{ "type": "color", "id": "bg_color", "label": "Background color", "default": "#ffffff" }
],
"presets": [{ "name": "Custom Hero" }]
}
{% endschema %}Once this file exists in the theme, your content team can add it to any page in the theme editor, fill in the settings, and see a live preview — no code changes needed.
Custom Section 1: Dynamic Product Showcase With Filters
The default Shopify collection page shows products in a grid. What if you want a feature section that shows "Best Sellers in a specific category" or "New arrivals this week" pulled dynamically from your catalog?
A developer can build a section that:
- Takes a collection handle as a setting
- Sorts by best-selling, newest, or custom sort order
- Shows a limited number of products (4, 6, 8, etc.)
- Displays variant swatches and real-time availability
- Links to collection with correct filters pre-applied
{% assign feature_collection = collections[section.settings.collection] %}
{% assign sort_by = section.settings.sort_by | default: 'best-selling' %}
{% assign product_limit = section.settings.product_count | default: 4 %}
{% assign sorted_products = feature_collection.products
| sort: sort_by
| limit: product_limit %}
<section class="dynamic-showcase">
<div class="showcase-header">
<h2>{{ section.settings.heading }}</h2>
<a href="{{ feature_collection.url }}">View all</a>
</div>
<div class="product-grid" data-columns="{{ section.settings.columns }}">
{% for product in sorted_products %}
{% render 'product-card', product: product %}
{% endfor %}
</div>
</section>
{% schema %}
{
"name": "Dynamic Product Showcase",
"settings": [
{ "type": "text", "id": "heading", "label": "Section heading" },
{ "type": "collection", "id": "collection", "label": "Collection to feature" },
{
"type": "select",
"id": "sort_by",
"label": "Sort products by",
"options": [
{ "value": "best-selling", "label": "Best selling" },
{ "value": "created-descending", "label": "Newest" },
{ "value": "price-ascending", "label": "Price: Low to High" }
],
"default": "best-selling"
},
{
"type": "range",
"id": "product_count",
"label": "Number of products",
"min": 2, "max": 12, "step": 2, "default": 4
}
],
"presets": [{ "name": "Dynamic Product Showcase" }]
}
{% endschema %}Your marketing team can change which collection is featured, how many products show, and the sort order — directly in the editor, no developer needed after it's built.
Custom Section 2: Product Comparison Table
If you sell products in different tiers — starter, professional, enterprise, or good/better/best — a comparison table is one of the highest-converting sections you can have on a collection or landing page.
No standard Shopify theme includes a dynamic comparison table. Apps provide them but charge monthly. A custom section does it better:
{% comment %} sections/comparison-table.liquid {% endcomment %}
{% assign compare_products = section.blocks
| where: "type", "product_column" %}
<section class="comparison-table">
<h2>{{ section.settings.heading }}</h2>
<div class="table-scroll">
<table>
<thead>
<tr>
<th>Features</th>
{% for block in compare_products %}
{% assign prod = all_products[block.settings.product] %}
<th>
<img src="{{ prod.featured_image | image_url: width: 200 }}" alt="{{ prod.title }}">
<span>{{ prod.title }}</span>
<span class="price">{{ prod.price | money }}</span>
<a href="{{ prod.url }}" class="btn">Buy Now</a>
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for feature_block in section.blocks %}
{% if feature_block.type == "feature_row" %}
<tr>
<td>{{ feature_block.settings.feature_name }}</td>
{% for col in compare_products %}
<td>{{ col.settings['feature_' | append: forloop.index] }}</td>
{% endfor %}
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</section>
{% schema %}
{
"name": "Comparison Table",
"max_blocks": 20,
"settings": [
{ "type": "text", "id": "heading", "label": "Section heading" }
],
"blocks": [
{
"type": "product_column",
"name": "Product",
"settings": [
{ "type": "product", "id": "product", "label": "Product" }
]
},
{
"type": "feature_row",
"name": "Feature row",
"settings": [
{ "type": "text", "id": "feature_name", "label": "Feature name" },
{ "type": "text", "id": "feature_1", "label": "Column 1 value" },
{ "type": "text", "id": "feature_2", "label": "Column 2 value" },
{ "type": "text", "id": "feature_3", "label": "Column 3 value" }
]
}
],
"presets": [{ "name": "Comparison Table" }]
}
{% endschema %}The content team adds products and feature rows in the editor. The table updates visually in real time. No app subscription, no performance overhead.
Custom Section 3: Before/After Image Slider
For stores selling anything with visible results — cleaning products, skincare, landscaping, home renovation — a before/after slider is compelling social proof that a static image can't replicate.
This requires a small amount of JavaScript to handle the drag interaction:
<div
class="before-after-slider"
data-before="{{ section.settings.before_image | image_url: width: 1200 }}"
data-after="{{ section.settings.after_image | image_url: width: 1200 }}"
>
<div class="after-image">
<img src="{{ section.settings.after_image | image_url: width: 1200 }}"
alt="{{ section.settings.after_label }}">
<span class="label after-label">{{ section.settings.after_label }}</span>
</div>
<div class="before-image">
<img src="{{ section.settings.before_image | image_url: width: 1200 }}"
alt="{{ section.settings.before_label }}">
<span class="label before-label">{{ section.settings.before_label }}</span>
</div>
<div class="slider-handle" role="slider" aria-label="Before/after comparison">
<div class="handle-line"></div>
<div class="handle-icon">⟺</div>
</div>
</div>The JavaScript implementation uses pointer events for cross-device compatibility (mouse, touch, and stylus). 60fps drag interaction with CSS clip-path for smooth reveal. About 80 lines of code total — no library, no app.
What Custom Sections Replace
When I build custom sections for a store, they typically replace:
| Feature | App cost/month | Custom section (one-time) |
|---|---|---|
| Before/after slider | $8–$15 | 4 hours developer time |
| Product comparison table | $15–$25 | 1 day developer time |
| Interactive FAQ accordion | $7–$12 | 2 hours developer time |
| Social proof ticker | $9–$18 | 3 hours developer time |
| Countdown timer | $8–$15 | 2 hours developer time |
| Video section with custom layout | $10–$20 | 3 hours developer time |
The break-even on custom development versus app subscriptions for these features is typically 2–4 months. After that, you're ahead every month — and your store loads faster because there are fewer external scripts.
The Right Sections to Build Custom
Not everything is worth custom development. A complex email marketing integration, a subscription billing system, a product review platform — these are genuinely complex services with ongoing operational costs. Keep the apps for these.
But any UI feature that: lives on your storefront, your content team needs to edit regularly, and costs money monthly as an app — is worth evaluating for custom development.
If you have sections you wish your theme had, or features you're paying app subscriptions for that could be built natively, I can scope what each one would take and whether the development investment makes sense for your store.




