Skip to content

Implementing a ‘read more/less’ feature for text blocks on Shopify

On-page content is a major part of SEO, particularly when it comes to category pages. Without text, search engines can struggle to understand what a page is about, which can make it difficult it to rank well in search results.

This often means we add introductory paragraphs to our category pages. Here’s a an example from our standard wireframe.

With Shopify though, this isn’t always straightforward. One common approach is to create two separate text boxes for the condensed and extended versions of your text. While feasible, this method may require adding an extra metafield for the second block, resulting in a complicated and disorganised setup.

Alternatively, embedding HTML into your text box to handle the text versions can also lead to a cluttered outcome. Additionally, if your store contains a large number of products or collections that require individual editing, the process becomes even more troublesome.

Our approach is a more streamlined solution requiring minor modifications to a Liquid file. This specifically targets the collection description.

Locate the Liquid file containing the text you wish to split.

Find the code displaying your collection description. It typically looks like:

{{ collection.description }}.

Wrap the above tag within the following HTML:

<div id="blink-collection-pad">
<span id="blink-collection-desc" style="display:inline;">{{ collection.description }}</span>
<span id="blink-links">
<a href="#" class="blink-more" style="display:none;">Read more</a>
<a href="#" class="blink-less" style="display:none;">Read less</a>

This code embeds the ‘Read More’ and ‘Read Less’ links, and provides class names and an ID for further manipulation.

Add a few styles to format the links and set initial states. This can be done inline or within a separate CSS file. The CSS code should resemble:

.blink-more, .blink-less { display: none; }
#blink-collection-desc, #blink-links { display: inline; }

The final step involves adding a JavaScript script, which is explained in detail below. Ideally, include this in a separate JS file or inline. The script includes a couple of configurable variables:

var limit = 10;
var breakpoint = 1024;

The limit variable defines the word count before the ‘READ MORE’ button appears, hiding the remainder of the text until clicked.

The breakpoint variable sets the screen width at which the script starts functioning. For instance, to display the full text on desktop views but truncate on mobile, set the breakpoint to 1024 pixels. Consequently, when the screen width is 1025 pixels or wider, the full text is visible without a ‘READ MORE’ link. Conversely, for screen widths of 1024 pixels or less, the text is truncated.

The script is designed to ensure that it never breaks the text in the middle of a link, preserving any hrefs within your text without breaking the functionality.

Entire code:

.blink-more, .blink-less { display: none; }
#blink-collection-desc, #blink-links { display: inline; }

<div id="blink-collection-pad">
<span id="blink-collection-desc" style="display:inline;"> {{ collection.description }}</span><span id="blink-links"><a href="#" class="blink-more" style="display:none;">Read more</a><a href="#" class="blink-less" style="display:none;">Read less</a></span>


function handleTextTruncation() {
// Wrap the whole function in a try...catch block to handle any runtime errors.
try {
// Initialize the ID and classes for elements, breakpoints, and limits
var outerContainerId = 'blink-collection-pad';
var textContainerId = 'blink-collection-desc';
var moreSelector = '.blink-more';
var lessSelector = '.blink-less';
var limit = 10;
var breakpoint = 1024;

// Get the container, text and action elements from the DOM
var outercontainer = document.getElementById(outerContainerId);
var container = document.getElementById(textContainerId);
var originalText = container.innerHTML;
var text = originalText.split(' ');
var moreLink = document.querySelector(moreSelector);
var lessLink = document.querySelector(lessSelector);

// Define a helper function to truncate the text
function truncate() {
var limitedText = '';
// Check if window's innerWidth is less than or equal to the breakpoint
if (window.innerWidth <= breakpoint) {
var node = container.cloneNode(true);
var words = node.textContent.split(' ');
// Check if the number of words is greater than the limit
if (words.length > limit) {
var currentWords = 0;
var newText = document.createElement('div');
// Iterate over all child nodes
for (var child of node.childNodes) {
// Stop the iteration if the currentWords count exceeds the limit
if (currentWords >= limit) break;
// Check if it's a text node
if (child.nodeType === 3) {
var childWords = child.textContent.split(' ');
// If adding the current node would exceed the limit, then trim the text
if (currentWords + childWords.length > limit) {
child.textContent = childWords.slice(0, limit - currentWords).join(' ') + '... ';
currentWords += childWords.length;
} else { // If it's not a text node, then it's an Element node
currentWords += child.textContent.split(' ').length;
// Add the child (text or element) to the newText
// Store the final limited text
limitedText = newText.innerHTML;
// Update the container's content with the limited text
container.innerHTML = limitedText;
// Show the moreLink and hide the lessLink = 'inline'; = 'none'; = 'block';
} else {
// If window's innerWidth is more than the breakpoint, then show the original text
container.innerHTML = originalText;
// Hide the moreLink and lessLink = 'none'; = 'none'; = 'block';

// Add onclick event handlers for the moreLink and lessLink
moreLink.onclick = function(e) {
container.innerHTML = originalText; = 'none'; = 'inline';

lessLink.onclick = function(e) {

// Call the truncate function once the window loads, and each time the window is resized.
window.onload = truncate;
window.onresize = truncate;

} catch (error) {
// In case of any error, reset the container's content to the original text
// and hide the moreLink and lessLink, then make the outercontainer visible.
container.innerHTML = originalText; = 'none'; = 'none'; = 'block';





Get in touch

Have a problem that Blink can help with? Let us know more about your project below and we’ll be in contact as soon as we can.