# Shopify Customer Login Status This client-side implementation provides a deterministic reference for customer login status in Shopify. Open the flems example by pressing the badge to see it in action: [![Flems](https://img.shields.io/badge/flems-example-playground?labelColor=34454d&color=999&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjUiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyNSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIzLjY2NCA5LjE2TDE4LjIxNiAzLjczNkMxOC4xMiAzLjYxNiAxNy45NTIgMy41NDQgMTcuODA4IDMuNTQ0SDE3LjEzNkMxNi4zMiAzLjU0NCAxNS41NzYgNC4wNzIgMTUuMzEyIDQuODY0TDE0LjI4IDcuOTZDMTQuMjA4IDguMiAxNC4zNzYgOC40NjQgMTQuNjQgOC40NjRIMTYuNDY0QzE2LjcyOCA4LjQ2NCAxNi44OTYgOC43MDQgMTYuODI0IDguOTQ0TDE2LjEwNCAxMS4xMjhDMTYuMDA4IDExLjM2OCAxNS43OTIgMTEuNTM2IDE1LjUyOCAxMS41MzZIMTMuNTM2QzEzLjI3MiAxMS41MzYgMTMuMDU2IDExLjcwNCAxMi45NiAxMS45NDRMMTEuNDk2IDE2LjM4NEMxMS4wNCAxNy43MjggMTAuMDMyIDE4Ljc2IDguNzU5OTkgMTkuMjRDOS4wMjM5OSAxOS41MjggOS4yNjM5OSAxOS41MjggOS40MzE5OSAxOS41MjhIMTQuNzEyQzE0Ljg4IDE5LjUyOCAxNS4wMjQgMTkuNDggMTUuMTQ0IDE5LjM2TDIzLjY2NCAxMC44NEMyNC4xMiAxMC4zODQgMjQuMTIgOS42MTYgMjMuNjY0IDkuMTZaTTkuNjk1OTkgMTIuMDRDOS43Njc5OSAxMS44IDkuNTc1OTkgMTEuNTM2IDkuMzM1OTkgMTEuNTM2SDcuNTM1OTlDNy4yOTU5OSAxMS41MzYgNy4xMjc5OSAxMS4yOTYgNy4xOTk5OSAxMS4wOEw3Ljk0Mzk5IDguODcyQzguMDE1OTkgOC42MDggOC4yMzE5OSA4LjQ2NCA4LjQ5NTk5IDguNDY0SDEwLjQ0QzEwLjcwNCA4LjQ2NCAxMC45MiA4LjI5NiAxMS4wMTYgOC4wNTZMMTIuNDggMy42MTZDMTIuOTEyIDIuMjcyIDEzLjk0NCAxLjI0IDE1LjIxNiAwLjc2QzE1LjEzMjkgMC42NjQ0NTcgMTUuMDI5MyAwLjU4ODkyNiAxNC45MTMgMC41MzkwNTRDMTQuNzk2NiAwLjQ4OTE4MyAxNC42NzA1IDAuNDY2MjYgMTQuNTQ0IDAuNDcySDkuMjg3OTlDOS4xNDM5OSAwLjQ3MiA4Ljk3NTk5IDAuNTIgOC44Nzk5OSAwLjY0TDAuMzU5OTkgOS4xNkMwLjI0ODIzNyA5LjI2OTUgMC4xNTk0NTYgOS40MDAxOSAwLjA5ODg0NzEgOS41NDQ0M0MwLjAzODIzNzkgOS42ODg2NiAwLjAwNzAxOTA0IDkuODQzNTUgMC4wMDcwMTkwNCAxMEMwLjAwNzAxOTA0IDEwLjE1NjUgMC4wMzgyMzc5IDEwLjMxMTMgMC4wOTg4NDcxIDEwLjQ1NTZDMC4xNTk0NTYgMTAuNTk5OCAwLjI0ODIzNyAxMC43MzA1IDAuMzU5OTkgMTAuODRMNS43ODM5OSAxNi4yNjRDNS45MDM5OSAxNi4zODQgNi4wNDc5OSAxNi40NTYgNi4xOTE5OSAxNi40NTZINi44Mzk5OUM3LjY1NTk5IDE2LjQ1NiA4LjM5OTk5IDE1LjkwNCA4LjY2Mzk5IDE1LjEzNkw5LjY5NTk5IDEyLjA0WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==)](https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQAWALmLCNgMYD2ZT8vyECAC+mclRr0AVkU48+A2lC4cArtV51lajUwCiCXVopMKBeEzpqCTLtQBOAAgC8jgORN7q+G4A6ZfwB5ACMpeA5LKHg8MngABXsuAAd4eyYATwAKAHcIMmVszHdrWwc3IuB-R2rHbjI8AHNVewpghCRHHApYc0wqmuz7CFM2+A6unvg+shrHfnVU1vbO7t7+6oA3bu8OzIJVYJcAPkdKgJnZmrqbRxNPF3coEwoAWhK7VLcAbnXL66ZHIkuADXNoFpowboDPBdD8LpdajwbhRVEwGA9MvBYA8gUwAJTHOawOgNCwAQSYnggwVR8Eyd3sBOczPcnm831+s3+nQg9ngDwAHoSBXQEGQGmiXCyAMyOAD8jgFxDQAF0rN1YJklSgVUURUlkpk8QSOkrVerYJrtSq8XCEdVuVxguZ7BtUg9YtlHABZVEmCA8EIut32TJgP1MANkAiEs72y44LhOTLcuPx9PVDIpaYZjOmeykpic3PCYsZrg4Rzh0yRpEEtO5moQSuZLP8gCELLcDOptIIbgJdUjZG8dsbNXzhcYZgpVJpfHplMZjgAZCvHPtgsQUWjWxQCxYCQq3LAuA08m5HB0T2euKi3GrE-Y9BQOAxMng+bay5dS-Df9+5zxk6wapHQIGpG6mS4hUP6zD287wAQHRslMcE1AhtIAGL4Hw9gdNuS4qj+wiAT+zaOPSqLvsaG4WAAKhA1B3kwmRGoS7wOFoXCZDe55kAOZH-rMfJMM0MwNumhaOBBrqpOxkmNqJ4kyc6kGpGOGaiOh1TSaeDSklAeQKTplzKfYMw7jRmnptpwn2jwuyUNQRQcMERQENwKQeqolpMicim5ty0BCq4m7EM58BqkkqgEO+xBuR5XlRQSLyOCgNlKRYKnsc4JzhZFaoEEksAQBwdIhUUKCAY2dmNsouzwBsRT7g0-mnKZswUfSBaOJ2riqPk0R5PAUAEuFTWPkmL5vlqwqivwErosyrgAEyruu+qxe+rXGp1TYtk1UquHxF71vtCJWexCqbqKZ55HQT4ze+n7wCagJcMCdDmEws5DIhi6eEUHheD4NXjtUwhEuYHX2RDV1Hh9X0-X9vYLgywMTOYA5XhuBx3Q0LGPdNr4vbyb2ZSWpl-vGwhwr8pGZIF1T6XkBG6j++ksezDN4v4pH0+cAD0QuOHoAAaZLenEAAyeiOAAwoEAAi8sAEJ6DLgQAOr+P4P2McxqJse1kmQvwkQqOC+iGBbximD9VixaU7onVjPg2ZxYE8LxrMCUUOCDREUYmUB9rXFwCAE77Z6GY4eSOAAxI40rlI4aIQAQQmzKIsPxomXDXsEFAAF5+P+pH81VGBoHz5wG0x8AsSbsa-F79jcTH-Fp8zjgAI6qAKHQoAz1e11XKc10JDdG6xuUBW3zsfB3yhd2di82Mvnd8SxA4TwALFPevnO34FkGv-udEHtbn+dYcOkikfwNHfFxwnycoGnGdZ4LueSQXRd9zl0rifJeXEfanUvoHMgwcfYUDvm3R+Ud9JdzfjMROrV+wtSEiA-w3JQq1DAd7c+kC07QNgbfWGiDoxPxfvpNBSdHCrS-gwTOODYL-gAe4YuZd+Z1zwUQjuECub3iKPPKh-4BRGmoQQWhKDX6jRkveHBeJ2AgHMAgChRBaAAFYAAcSBVooBEGIEAkVBBWAILIEAQ4LaCBECqYQQA) ### Theme Layout In your `theme.liquid` or layout file, add the following data attribute to the `` element. While not required, it is **recommended** for general use, though the solution will work programmatically. ```liquid ``` ### Code Snippet Below is the JavaScript implementation. We use a Mutation Observer to efficiently interface with Liquid and ensure support for cases where partial DOM replacements occur. The global `window` definition and event-based (pub/sub) approach allow for multiple listeners and sequential operations across the webshop. ```js Object.defineProperty(window, 'customer', { configurable: false, writable: false, enumerable: false, value: (sub => { const attr = 'data-customer'; const root = document.documentElement; const auth = (el = root) => el.getAttribute(attr) === 'true'; const fire = x => x.length === 3 ? x[0].call(x[1], x.pop()) : x[0].call(x[1]); const observer = new MutationObserver(mutations => { for (const { type, target } of mutations) { if (type !== 'attributes') continue; target.hasAttribute(attr) && sub[auth(target) ? 'login' : 'logout'].forEach(fire); } }); observer.observe(root, { attributes: true, attributeFilter: [ attr ] }); if (auth()) setTimeout(() => customer.do('login')); return { get observer () { return observer; }, get loggedin () { return auth(); }, on: (name, cb, scope = null) => { const idx = sub[name].push([ cb, scope ]) - 1; return () => sub[name].splice(idx, 1); }, do: (ev, arg) => { if (arg !== undefined) sub[ev].forEach(x => x.length === 2 && x.push(arg)) if (ev === 'login') { auth() ? sub.login.forEach(fire) : root.setAttribute(attr, 'true'); } else { auth() ? root.setAttribute(attr, 'false') : sub.logout.forEach(fire); } } }; })({ login: [], logout: [] }) }); ``` Here is a minified version of the above code: ```js Object.defineProperty(window,"customer",{configurable:!1,writable:!1,enumerable:!1,value:(t=>{const e="data-customer",o=document.documentElement,r=(t=o)=>"true"===t.getAttribute(e),u=t=>3===t.length?t[0].call(t[1],t.pop()):t[0].call(t[1]),n=new MutationObserver((o=>{for(const{type:n,target:l}of o)"attributes"===n&&l.hasAttribute(e)&&t[r(l)?"login":"logout"].forEach(u)}));return n.observe(o,{attributes:!0,attributeFilter:[e]}),r()&&setTimeout((()=>customer.do("login"))),{get observer(){return n},get loggedin(){return r()},on:(e,o,r=null)=>{const u=t[e].push([o,r])-1;return()=>t[e].splice(u,1)},do:(n,l)=>{void 0!==l&&t[n].forEach((t=>2===t.length&&t.push(l))),"login"===n?r()?t.login.forEach(u):o.setAttribute(e,"true"):r()?o.setAttribute(e,"false"):t.logout.forEach(u)}}})({login:[],logout:[]})}); ``` # API / Usage Simply drop the JavaScript into your theme (bundle or wherever) and use the `customer.on` method. Below is the complete API and usage details: ### `customer.on(event, callback, this)` This function expects 2 parameters and optionally accepts a 3rd `this` context binding. There are only 2 available subscribers: `customer.on('login')` and/or `customer.on('logout')`. Examples: ```js // Basic login callback customer.on('login', () => console.log('logged in')); // Login callback with "this" scope customer.on('login', function() { console.log(this); }, { foo: 'bar' }); // Basic logout callback customer.on('logout', () => console.log('logged out')); // Logout callback with "this" scope customer.on('logout', function() { console.log(this); }, { foo: 'bar' }); ``` #### Removing Event Subscriptions If you want to remove subscriptions, each `customer.on` event returns a function, simply call it and to remove it from the sub store, for example: ```js const a = customer.on('login', () => console.log('logged in #1')) const b = customer.on('login', () => console.log('logged in #2')) // We can remove subscription a as follows: a() // Now, if you were to trigger calls only b (logged in #2) will fire customer.do('login'); // You can optionally trigger a once callback by doing: customer.on('login', () => console.log('logged in #3'))() ``` --- ### `customer.do(event, ...arguments)` This method programmatically triggers subscribed events. It expects a string value of either `login` or `logout` as its first parameter. Optionally, you can pass additional arguments that subscribed events will receive. ```js // Trigger login events customer.do('login'); // Trigger logout events customer.do('logout'); // Passing data to subscription events customer.on('login', (a, b, c) => console.log(a, b, c)); // When calling customer.do, pass data as follows: customer.do('login', 'foo', {x: 1}, ['xxx']); ``` --- ### `customer.loggedin` A **readonly** getter that returns a boolean value indicating if the customer is logged in. ```js if (customer.loggedin) console.log('logged in!'); ``` --- ### `customer.observer` Provides access to the Mutation Observer instance. --- # Author Follow me on [X](https://x.com/niksavvidis) if you like Shopify and banter. ### License You are not allowed to use this in themes published to theme store. Everyone else, you are fine, DWTFYW.