This guide will walk you through creating a "Previously Viewed" product block using PersonalizeWP's REST API. This personalisation feature enhances user experience by showing visitors products they've previously viewed on your WooCommerce store.
Overview
The "Previously Viewed" block displays products that the current visitor has previously viewed on your website. This implementation uses the /visitor-activities endpoint to retrieve the visitor's viewing history, filtered by the current post type.
Prerequisites
- PersonalizeWP Pro installed and activated
- Basic knowledge of JavaScript and WordPress development
- Access to your theme files or a custom plugin where you can add JavaScript code
Implementation Steps
Step 1: Create the HTML Structure
First, create the HTML structure for your "Previously Viewed" block. You can add this to your theme or plugin as a snippet in a template or you can adapt it to use in a custom Block Editor block:
Previously Viewed
Step 2: Fetch Previously Viewed Products
Create a JavaScript function to fetch and display the previously viewed products using the PersonalizeWP REST API (you do not need to authenticate to the API from your own site):
NOTE: The UID is an obfuscated non-incrementing ID that is stored on the visitors local browser storage and specific to their interaction with your site.
function initPreviouslyViewedBlock() { // Get the container element const container = document.getElementById('pwp-previously-viewed-container');
// If the container doesn't exist, exit early if (!container) return;
// Get the current post type and ID const currentPostType = document.body.classList.contains('single-product') ? 'product' : null; const currentPostId = document.querySelector('[data-product-id]')?.dataset.productId;
// Only proceed if we're on a product page if (!currentPostType || !currentPostId) { container.closest('.pwp-previously-viewed').style.display = 'none'; return; }
// Fetch previously viewed products fetchPreviouslyViewedProducts(currentPostType, currentPostId, container); }
async function fetchPreviouslyViewedProducts(currentPostType, currentPostId, container) { const PWP = localStorage.getItem('pwptrackeduser') || ''; const UID = (PWP) ? JSON.parse(PWP).id : null;
if (!UID) { container.closest('.pwp-previously-viewed').style.display = 'none'; return; }
const baseURL = window.pwpSettings?.root || '/wp-json/'; const namespace = 'personalizewp/v1/'; const endpoint = 'visitor-activities';
// Set up filters to get only viewed products excluding the current one const filters = [ { activitytype: 'view' }, { objecttype: currentPostType } ];
try { const response = await fetch(${baseURL}${namespace}${endpoint}, { method: "POST", cache: "no-cache", headers: { "Accept": "application/json, /;q=0.1", "Cache-Control": "no-cache, private", "Content-type": "application/json", "X-Requested-With": "XMLHttpRequest", }, body: JSON.stringify({ uid: UID, filters: filters }), });
if (!response.ok) { throw new Error('Network response was not ok'); }
const activities = await response.json();
// Filter out current product and duplicates const uniqueProductIds = new Set(); const filteredActivities = activities.filter(activity => { // Skip current product if (activity.object_id.toString() === currentPostId.toString()) { return false; }
// Skip duplicates if (uniqueProductIds.has(activity.object_id)) { return false; }
uniqueProductIds.add(activity.object_id); return true; });
// Display the products or hide the container if none found if (filteredActivities.length > 0) { displayPreviouslyViewedProducts(filteredActivities, container); } else { container.closest('.pwp-previously-viewed').style.display = 'none'; } } catch (error) { console.error('Error fetching previously viewed products:', error); container.closest('.pwp-previously-viewed').style.display = 'none'; } }
async function displayPreviouslyViewedProducts(activities, container) { // Clear the container container.innerHTML = '';
// Limit to 3 products const limitedActivities = activities.slice(0, 3);
try { // Fetch product data using WooCommerce REST API or a custom endpoint const productIds = limitedActivities.map(activity => activity.object_id); const productsData = await fetchProductsData(productIds);
// Create and append product elements productsData.forEach(product => { const productElement = createProductElement(product); container.appendChild(productElement); });
// Show the container container.closest('.pwp-previously-viewed').style.display = 'block'; } catch (error) { console.error('Error displaying previously viewed products:', error); container.closest('.pwp-previously-viewed').style.display = 'none'; } }
// Helper function to fetch product data async function fetchProductsData(productIds) { // This is a simplified example. In a real implementation, you would fetch // the product data from your WordPress site using REST API or AJAX.
// For WooCommerce, you might use the WC API: const baseURL = '/wp-json/wc/v3/products'; const queryParams = new URLSearchParams({ include: productIds.join(','), consumerkey: 'yourconsumerkey', // You would use proper authentication consumersecret: 'yourconsumersecret' });
// In a real implementation, use proper authentication const response = await fetch(${baseURL}?${queryParams}); return await response.json(); }
// Helper function to create a product element function createProductElement(product) { const productDiv = document.createElement('div'); productDiv.className = 'pwp-product-item';
productDiv.innerHTML = `
${product.name}
return productDiv; }
// Initialize the block when DOM is ready document.addEventListener('DOMContentLoaded', initPreviouslyViewedBlock);
Step 3: Add CSS Styling
Add CSS to style your "Previously Viewed" block:
.pwp-previously-viewed { margin: 2rem 0; padding: 1rem; background-color: #f9f9f9; border-radius: 4px; }
.pwp-previously-viewed h2 { margin-bottom: 1rem; font-size: 1.5rem; color: #333; }
.pwp-previously-viewed-products { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1rem; }
.pwp-product-item { border: 1px solid #eee; border-radius: 4px; padding: 1rem; background-color: white; transition: transform 0.2s, box-shadow 0.2s; }
.pwp-product-item:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); }
.pwp-product-image img { width: 100%; height: auto; object-fit: cover; border-radius: 3px; }
.pwp-product-title { margin: 0.5rem 0; font-size: 1rem; font-weight: 600; }
.pwp-product-price { color: #555; margin-bottom: 0.5rem; }
.pwp-add-to-cart { width: 100%; padding: 0.5rem; background-color: #333; color: white; border: none; border-radius: 3px; cursor: pointer; transition: background-color 0.2s; }
.pwp-add-to-cart:hover { background-color: #555; }
Step 4: Enqueue Your Script and Styles
Add this to your theme's functions.php file or your custom plugin:
function pwpenqueuepreviouslyviewedscripts() { // Only enqueue on product pages if (isproduct()) { wpenqueuescript( 'pwp-previously-viewed', gettemplatedirectoryuri() . '/assets/js/previously-viewed.js', array('jquery'), '1.0.0', true );
wpenqueuestyle( 'pwp-previously-viewed-styles', gettemplatedirectory_uri() . '/assets/css/previously-viewed.css', array(), '1.0.0' );
// Pass the WordPress REST API root URL to JavaScript wplocalizescript( 'pwp-previously-viewed', 'pwpSettings', array( 'root' => escurlraw(resturl()), 'nonce' => wpcreatenonce('wprest') ) ); } } addaction('wpenqueuescripts', 'pwpenqueuepreviouslyviewed_scripts');
Step 5: Add the Block to Your Theme
Insert the HTML structure in your theme's appropriate template file. For WooCommerce, you might add it to single-product.php or use a hook:
function pwpaddpreviouslyviewedblock() { echo '<div class="pwp-previously-viewed">