Shopify コーディング

Shopifyに最強の「閲覧履歴」機能をアプリなしで実装!コピペで動く決定版コードを完全解説

Shopifyストアを運営しているなら、一度はこう考えたことがあるのではないでしょうか?

「一度チェックした商品を、トップページでさりげなくお客様に再アピールしたい」

いわゆる「閲覧履歴」機能は、お客様の購買意欲を高め、ストアの回遊率を上げるための非常に強力な武器です。
しかし、高機能なアプリは月額費用がかかるし、サイトが重くなるのも避けたいですよね。
最近のテーマでは既存であるものが多いですが、昔のテーマはない場合もあります。

ご安心ください!この記事では、そんな悩みを一発で解決します。

アプリを使わずに、無料で、かつ高機能な「閲覧履歴」セクションを追加する方法を、
コピー&ペーストするだけで動く「決定版」のコードと共に、どこよりも詳しく解説します。
※そのまま動くかはテーマに寄るので、テーマに合わせて改善してください。

この記事を最後まで読めば、あなたのストアにも、人気商品セクションとデザインが完全に調和した、プロ仕様の閲覧履歴機能が実装されているはずです。

完成イメージ:デザインと機能を両立した理想の閲覧履歴

今回実装する閲覧履歴セクションの完成イメージがこちらです。

PCでの表示:自動スライダーで回遊性アップ!

PCでは、人気商品セクションなどと完全にトンマナ(デザインの雰囲気)を合わせた、自動で横に流れるスライダー形式を採用。お客様の目に留まりやすく、かつスペースを有効活用できます。

PCでは他のセクションとデザインが統一されたスライダーとして表示

テーマカスタマイザー:直感的な操作で設定変更

見出しの文言はもちろん、表示する商品の最大件数、スライダーを自動で動かすかどうか、その速度まで、すべてShopifyの管理画面からノーコードで直感的に設定できるようにします。

管理画面で見出し、スライド速度等カスタマイズ可能

実装は簡単3ステップ!

「なんだか難しそう…」と思った方もご安心ください。実装は驚くほどシンプルで、以下の3つのステップで完了します。

  1. 新しいセクションファイルを作成する
  2. 完成版コードをコピペする
  3. テーマカスタマイザーで「2箇所」に設置する【最重要】

それでは、一つずつ見ていきましょう。

ステップ1:閲覧履歴用のセクションファイルを作成する

まずは、閲覧履歴機能のプログラムを格納するための、新しい「城」となるセクションファイルを用意します。

  1. Shopify管理画面から [オンラインストア] > [テーマ] に進みます。
  2. 現在編集しているテーマの […] (アクション) をクリックし、[コードを編集] を選択します。
  3. 左側のファイル一覧から [Sections] フォルダを見つけ、その中にある [NEW FILE] ボタンをクリックします。
  4. ファイル名を入力する空白が表示されたら、「recent-products」と入力し、[完了] をクリックします。(名前は基本的に何でもOKですが、分かりやすいものがおすすめです)

これで、sections/recent-products.liquid という、プログラムを書き込むための空っぽのファイルが作成されました。

ステップ2:完成版コードをコピー&ペーストする

次はこのブログ記事のメインです。
先ほど作成した recent-products.liquid の中身を一度すべて削除し、以下の完成版コードを丸ごとコピーして、貼り付けてください。


{%- comment -%}
商品ページで画像情報を取得するための準備
{%- endcomment -%}
{%- assign featured_image = product.featured_media | default: product.featured_image -%}
{%- if template.name == 'index' or template.name == 'cart' -%}
<section class="recently-viewed-wrapper">
<div class="recently-viewed-wrap">
<div class="sec-head">
<h2 class="top-sec-ttl">
<span lang="en">{{ section.settings.title_en | escape }}</span>
<span lang="ja">{{ section.settings.title_ja | escape }}</span>
</h2>
<p class="top-sec-txt">{{ section.settings.text | escape }}</p>
</div>
<div class="recently-viewed-slide sec-slide">
<!-- JavaScriptによって商品がここに挿入されます -->
</div>
</div>
</section>
{%- endif -%}
<script>
document.addEventListener("DOMContentLoaded", () => {
const localStorageKey = "{{ shop.domain | append: '_recentlyedProduct'}}";
let productObject = null;
try {
productObject = {{ product | json }};
} catch (e) {}
// 1. 商品ページでのみ、履歴を「保存」する処理
if (productObject && productObject.id) {
const productDataForSave = {
productTitle: {{ product.title | json }},
productImg: "{{ featured_image | image_url: width: 300, height: 300, crop: 'center' }}",
productPriceRaw: {{ product.price }},
productPrice: "{{ product.price | money }}",
productUrl: "{{ shop.url }}{{ product.url }}",
productImageAltText: {{ featured_image.alt | json }}
};
try {
let currentHistory = JSON.parse(localStorage.getItem(localStorageKey));
if (!Array.isArray(currentHistory)) { currentHistory = []; }
const isAlreadyInList = currentHistory.some(item => item.productUrl === productDataForSave.productUrl);
if (!isAlreadyInList) {
const maxProducts = {{ section.settings.max_product_count }};
if (currentHistory.length >= maxProducts) {
currentHistory.shift();
}
currentHistory.push(productDataForSave);
localStorage.setItem(localStorageKey, JSON.stringify(currentHistory));
}
} catch (e) {
localStorage.setItem(localStorageKey, JSON.stringify([productDataForSave]));
}
}
// 2. 表示コンテナが存在する場合のみ、履歴を「表示」する処理
const container = document.querySelector('.recently-viewed-wrapper');
if (container) {
try {
const productData = JSON.parse(localStorage.getItem(localStorageKey));
if (!Array.isArray(productData) || productData.length === 0) {
container.style.display = 'none';
return;
}
const productListElement = container.querySelector(".recently-viewed-slide");
const reversedProductData = [...productData].reverse();
const productHtml = reversedProductData.map(item => {
const priceFormatted = item.productPrice.replace('¥', '').replace('.00', '');
let priceWithTax;
if (item.productPriceRaw) {
priceWithTax = (Math.floor(item.productPriceRaw * 1.10)).toLocaleString();
} else {
const priceAsNumber = parseInt(priceFormatted.replace(/,/g, ''), 10);
priceWithTax = (Math.floor(priceAsNumber * 1.10)).toLocaleString();
}
return `
<div class="recently-viewed-slide-item">
<a href="${item.productUrl}">
<div class="recently-viewed-item-img">
<img src="${item.productImg}" width="188" height="188" loading="lazy" alt="${item.productImageAltText || ''}" />
</div>
<h3 class="recently-viewed-item-name">${item.productTitle}</h3>
<div class="price__regular">
<span class="price-item price-item--regular">
${priceFormatted} <span>円</span>
</span>
</div>
<div class="price__taxes-included">
<span class="price-item price-item--taxes-included">
${priceWithTax} <span>円(税込)</span>
</span>
</div>
</a>
</div>
`;
}).join('');
productListElement.innerHTML = productHtml;
if (typeof $ !== 'undefined' && typeof $.fn.slick !== 'undefined') {
if ($(".recently-viewed-slide").hasClass('slick-initialized')) {
$(".recently-viewed-slide").slick('unslick');
}
$(".recently-viewed-slide").slick({
slidesToShow: 6,
slidesToScroll: 1,
autoplay: {{ section.settings.autoplay }},
autoplaySpeed: {{ section.settings.autoplay_speed }},
speed: 700,
arrows: true,
dots: false,
responsive: [ { breakpoint: 896, settings: { infinite: true, centerMode: true, centerPadding: '20%', slidesToShow: 1, autoplay: false, arrows: false, dots: false, }, }, ],
});
}
} catch (e) {
container.style.display = 'none';
}
}
});
</script>
{% schema %}
{
"name": "閲覧履歴",
"tag": "section",
"class": "section",
"limit": 1,
"settings": [
{
"type": "header",
"content": "コンテンツ設定"
},
{
"type": "text",
"id": "title_en",
"default": "RECENTLY VIEWED",
"label": "見出し(英語)"
},
{
"type": "text",
"id": "title_ja",
"default": "最近チェックした商品",
"label": "見出し(日本語)"
},
{
"type": "text",
"id": "text",
"default": "お客様が最近チェックした商品はこちら",
"label": "説明テキスト"
},
{
"type": "range",
"id": "max_product_count",
"min": 6,
"max": 24,
"step": 1,
"unit": "商品",
"label": "最大表示(保存)数",
"default": 12
},
{
"type": "header",
"content": "スライダー設定"
},
{
"type": "checkbox",
"id": "autoplay",
"label": "自動でスライドする(PCのみ)",
"default": true
},
{
"type": "range",
"id": "autoplay_speed",
"min": 2000,
"max": 9000,
"step": 500,
"unit": "ミリ秒",
"label": "スライド切り替え速度",
"default": 4000
}
],
"presets": [
{
"name": "閲覧履歴"
}
]
}
{% endschema %}
<style>
.recently-viewed-wrapper { position: relative; border-bottom: 1px solid #e6e6e6; padding: 0 0 40px; margin: 0 0 40px; }
.recently-viewed-wrap {}
.recently-viewed-slide { padding: 0 36px; }
.recently-viewed-slide-item { margin: 0 14px 0 0; }
.recently-viewed-slide-item a { display: block; color: #333; text-decoration: none; }
.recently-viewed-item-img { display: flex; justify-content: center; align-items: center; min-height: 188px; margin: 0 0 8px; }
.recently-viewed-item-img img { display: block; max-width: 188px; max-height: 188px; width: 100%; height: auto; margin: 0 auto; transition: 0.3s; }
.recently-viewed-slide-item a:hover .recently-viewed-item-img img { opacity: 0.7; }
.recently-viewed-item-name { font-size: 13px; font-weight: normal; margin: 0 0 4px; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-height: 1.3; min-height: 2.6em; }
.recently-viewed-slide-item .price__regular .price-item--regular { font-family: "Roboto"; font-size: 20px; font-weight: bold; color: #D61212; letter-spacing: 0; }
.recently-viewed-slide-item .price__taxes-included .price-item--taxes-included { font-family: "Roboto"; font-size: 12px; color: #333; letter-spacing: 0; }
.recently-viewed-slide-item .price__regular .price-item--regular span { font-family: "Noto Sans JP"; font-size: 12px; }
.recently-viewed-slide-item .price__taxes-included .price-item--taxes-included span { font-family: "Noto Sans JP"; font-size: 10px; }
.recently-viewed-slide .slick-prev, .recently-viewed-slide .slick-next { top: 40%; transform: translateY(-50%); }
@media screen and (max-width: 896px) {
.recently-viewed-wrapper { padding: 0 0 32px; margin: 0 0 32px; }
.recently-viewed-slide { padding: 0; }
}
</style>

貼り付けたら、右上の[保存]ボタンを忘れずにクリックしてください。

ステップ3:テーマカスタマイザーで「2箇所」に設置する【最重要】

コードの設置が完了したら、いよいよサイトに表示させます。ここがこの実装で最も重要なポイントです!

この閲覧履歴機能は、「商品を保存する役目」と「保存した商品を表示する役目」を分担しているため、必ず2箇所に設置しないと正常に動作しません。

1. トップページに設置(表示役)

まず、お客様に実際に閲覧履歴を見せるためのセクションを設置します。

  1. テーマカスタマイザーを開き、トップページ(ホームページ)が表示されていることを確認します。(別のページ希望なら別ページ)
  2. 左側のセクション一覧から [セクションを追加] をクリックし、リストから先ほど作成した「閲覧履歴」セクションを選択します。
  3. ドラッグ&ドロップで、お好きな表示位置に移動させましょう。

2. 商品ページに設置(保存役)

次に、お客様が閲覧した商品を裏側でこっそり記録するためのセクションを設置します。

  1. カスタマイザー画面上部中央のドロップダウンメニューをクリックし、[商品] > [デフォルトの商品] を選択します。
  2. 商品ページの編集画面に切り替わったら、同じように左側の [セクションを追加] から「閲覧履歴」を追加します。
  3. 【ポイント】 こちらは商品を保存するだけの裏方なので、プレビュー画面には表示されません。セクションを追加する場所はどこでもOKです!

※商品テンプレートがデフォルト以外もある場合はすべて設定が必要です。

最後に、トップページと商品ページの両方で、追加した「閲覧履歴」セクションの設定(特に「最大表示(保存)数」)が同じになっていることを確認し、
右上の[保存]をクリックすれば、すべての作業は完了です!

【応用編】他のテーマで使う場合の注意点

このコードはShopifyの標準的な仕組みをベースにしているため、Dawnやその他多くのテーマで動作する可能性が高いです。
もし、他のストアや別のテーマでこのコードを使いたい場合は、以下の3点に注意すると、よりスムーズに移植できます。

  1. jQueryとSlick Sliderの確認
    このスライダーはjQueryとSlick Sliderというライブラリが必要です。ほとんどのテーマには初めから入っていますが、もしスライダーが動かない場合は、これらのライブラリがテーマに導入されているか確認してみてください。
  2. CSSクラス名の調整
    見出し(sec-headなど)や商品カードのデザインは、特定のテーマのCSSを参考にしています。別のテーマでデザインが崩れる場合は、このコードの<style>タグ内やHTMLのクラス名を、移植先のテーマのデザインに合わせて調整すると、より綺麗に馴染みます。
  3. 「2箇所に設置」を忘れずに!
    これが最も重要です。どんなテーマに移植する場合でも、必ず「トップページ(表示役)」「商品ページ(保存役)」の2箇所にセクションを設置してください。

まとめ

今回は、Shopifyにアプリなしで高機能な「閲覧履歴」機能を実装する方法を、コードのコピペだけで完成できるように解説しました。

この機能は、お客様の「あ、これさっき見たやつだ」という記憶を呼び覚まし、購入への最後の一押しを後押ししてくれる強力なマーケティングツールになります。

ぜひ、あなたのストアの回遊率アップや顧客体験の向上に役立ててみてください!

-Shopify, コーディング
-,

Copyright© ゆるふわなねこ , 2025 All Rights Reserved.