JavaScript design patterns - LogRocket Blog

[
Advisory boards aren’t only for executives. Join the LogRocket Content Advisory Board today →
](https://lp.logrocket.com/blg/content-advisory-board-signup).metrics-banner {padding: 8px 0px; background: #764abc ;color: #ffffff;} .metrics-banner h6 {color: #ffffff;text-align:center; margin: 0px !important;} #metrics-embed {width: 65.5%; margin: 0px auto; display: block;} .mediumnavigation {padding: 0px 0px 8px 0px;} @media all and (max-width: 800px){ .mediumnavigation{box-shadow: none !important;} #metrics-embed {width: 90%;} .mainheading {padding: 4rem 0 1rem 0;} }
2018-11-02
1953
#vanilla javascript
Mesh Seun
237
Nov 2, 2018 ⋅ 6 min read
JavaScript design patterns
Mesh Seun Software engineer.

Introducing Galileo AI
LogRocket’s Galileo AI watches every session, surfacing impactful user struggle and key behavior patterns.
const threshold = 2000; const isMobile = window.matchMedia("(max-width: 1280px)").matches; window.addEventListener('scroll', (event) => { if (isMobile) { return; } const gutter = document.querySelector(".plug-gutter"); // show it if it is hidden and we have scrolled past the threshold if (window.scrollY > threshold && !gutter.classList.contains("show")) { gutter.classList.add("show"); window.toggleToc(true); } // hide it if we scroll back above the threshold if (window.scrollY < threshold && gutter.classList.contains("show")) { gutter.classList.remove("show"); window.toggleToc(false); } });
.plug-dev-top--card { border: 2px solid rgba(118, 74, 188, 0.2); border-radius: 8px; background-color: #491d90; justify-content: space-between; align-items: center; padding: 16px; font-family: 'Proxima Nova', sans-serif; display:flex; gap: 1rem; cursor: pointer; } .plug-dev-top--logo { width: 48px; height: 48px; border-radius: 50%; } .plug-dev-top--blurb { h2 { font-weight: 800; font-size: 22px; line-height: 22px; color: #fff; width: 70%; margin: 0 0 5px 0; } h3 { font-weight: 900; font-size: 13px; line-height: 13px; letter-spacing: 0.1em; color: #fff; opacity: 0.5; white-space: nowrap; margin: 0; } } .plug-dev-top--cta { background-color: #fff; color: #764abc; border: none; border-radius: 4px; padding: 10px; font-size: 16px; font-weight: 800; font-family: 'Proxima Nova', sans-serif; text-decoration: none; width: 25%; text-align: center; &:hover { text-decoration: none; color: #a58ec8; } } .plug-dev-top--asset { position: relative; height: 400px; overflow: hidden; transition: all 0.3s ease; margin-top: 30px; &.lr-hidden { height: 0; margin: 0; } }

See how LogRocket's Galileo AI surfaces the most severe issues for you
No signup required
Check it out
const wistiaIframe = document.querySelector("#galileo-wistia-iframe"); wistiaIframe.addEventListener('load', () => { const player = wistiaIframe.wistiaApi; window.document.querySelector(".plug-dev-top").addEventListener('click', () => { const videoContainer = window.document.querySelector(".plug-dev-top--asset"); videoContainer.classList.toggle('lr-hidden'); if (videoContainer.classList.contains("lr-hidden")) { player.pause(); } else { player.play(); } }); });

Objective
This article will:
- Get you accustomed to Javascript patterns
- Explain why you should consider using design patterns in your next Javascript project
For some of the projects I built in the past, I only used Javascript patterns because I thought they looked fancy, not because they added anything meaningful to the experience.
I want to help you avoid the same mistake.
It might seem obvious, but there really should be a good reason for using a particular type of design pattern.
Why consider using a design pattern in Javascript?
A pattern is a reusable solution that can be applied to commonly occurring problems in software engineering.
Using a design pattern helps reduce the time spent on how the code should look.
Not only that, a good design pattern enforces the DRY (Do not Repeat Yourself) concept that helps to prevent your codebase from growing large and unwieldy.
Design patterns also help team members collaborate, especially in a situation where everyone on the team is familiar with the pattern in question. Every team member will definitely communicate better while handling a uniform style (pattern) in building up a project.
How can you recognize a good design pattern?
As funny as this may sound, a good pattern needs to have a name and a precise, detailed structure. This is not at all the same as mere habits.
Every developer has a way of solving a specific problem (file upload, for example). When a file needs to be handled in any of our projects, we gladly rush to implement this specific solution.
Can we call this a pattern? Definitely not. A good or accepted design pattern must be related with existing patterns. Not only that, it must be approved by other developers.
Examples of how the pattern can be used and a detailed documentation cannot be overemphasized.
[

](https://lp.logrocket.com/blg/learn-more)
[#replay-signup { margin-block: 1rem; display: flex; gap: 0.5rem; flex-direction: column; align-items: center; border: 1px solid #E6DFF6; border-radius: 16px; padding: 0.5rem 2rem; background-color: #F4F0FB; font-family: 'Proxima Nova', sans-serif; font-size: 0.95rem; h3 { margin: 0; font-weight: bold; } p { max-inline-size: 65ch; } .replay-signup-form-wrapper { width: 60%; } .nf-before-form-content { display: none; } input.nf-element[type="submit"] { background-color: #764abc; color: #fff; border-radius: 8px; padding-block: 10px; height: unset; width: 100%; display: block; } .nf-response-msg { font-size: 1.2rem; text-align: center; } }
🚀 Sign up for The Replay newsletter
](https://lp.logrocket.com/blg/learn-more)
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
Notice: JavaScript is required for this content.
var formDisplay=1;var nfForms=nfForms||[];var form=[];form.id='48';form.settings={"objectType":"Form Setting","editActive":true,"title":"Replay Newsletter Signup","show_title":0,"allow_public_link":0,"embed_form":"","clear_complete":1,"hide_complete":1,"default_label_pos":"above","wrapper_class":"","element_class":"","form_title_heading_level":"3","key":"","add_submit":0,"changeEmailErrorMsg":"Please enter a valid email address!","changeDateErrorMsg":"Please enter a valid date!","confirmFieldErrorMsg":"These fields must match!","fieldNumberNumMinError":"Number Min Error","fieldNumberNumMaxError":"Number Max Error","fieldNumberIncrementBy":"Please increment by ","formErrorsCorrectErrors":"Please correct errors before submitting this form.","validateRequiredField":"This is a required field.","honeypotHoneypotError":"Honeypot Error","fieldsMarkedRequired":"Fields marked with an *<\/span> are required","currency":"","unique_field_error":"A form with this value has already been submitted.","logged_in":false,"not_logged_in_msg":"","sub_limit_msg":"The form has reached its submission limit.","calculations":[],"conditions":[],"mp_breadcrumb":1,"mp_progress_bar":1,"mp_display_titles":0,"formContentData":[{"formContentData":["email","submit_1760469924554"],"order":0,"type":"part","clean":true,"title":"Part Title","key":"hzyqqobd"}],"objectDomain":"display","drawerDisabled":false,"ninjaForms":"Ninja Forms","fieldTextareaRTEInsertLink":"Insert Link","fieldTextareaRTEInsertMedia":"Insert Media","fieldTextareaRTESelectAFile":"Select a file","formHoneypot":"If you are a human seeing this field, please leave it empty.","fileUploadOldCodeFileUploadInProgress":"File Upload in Progress.","fileUploadOldCodeFileUpload":"FILE UPLOAD","currencySymbol":"$","thousands_sep":",","decimal_point":".","siteLocale":"en_US","dateFormat":"m\/d\/Y","startOfWeek":"1","of":"of","previousMonth":"Previous Month","nextMonth":"Next Month","months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"weekdays":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"weekdaysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"weekdaysMin":["Su","Mo","Tu","We","Th","Fr","Sa"],"recaptchaConsentMissing":"reCaptcha validation couldn't load.","recaptchaMissingCookie":"reCaptcha v3 validation couldn't load the cookie needed to submit the form.","recaptchaConsentEvent":"Accept reCaptcha cookies before sending the form.","currency_symbol":"","beforeForm":"","beforeFields":"","afterFields":"","afterForm":""};form.fields=[{"objectType":"Field","objectDomain":"fields","editActive":false,"order":999,"idAttribute":"id","type":"email","label":"Email","key":"email","label_pos":"hidden","required":1,"default":"","placeholder":"Email","container_class":"","element_class":"","admin_label":"","help_text":"","custom_name_attribute":"email","personally_identifiable":1,"value":"","manual_key":true,"drawerDisabled":false,"id":401,"beforeField":"","afterField":"","parentType":"email","element_templates":["email","input"],"old_classname":"","wrap_template":"wrap"},{"objectType":"Field","objectDomain":"fields","editActive":false,"order":999,"idAttribute":"id","type":"submit","label":"Submit","processing_label":"Submitting...","container_class":"","element_class":"","key":"submit_1760469924554","admin_label":"","drawerDisabled":false,"id":402,"beforeField":"","afterField":"","value":"","label_pos":"above","parentType":"textbox","element_templates":["submit","button","input"],"old_classname":"","wrap_template":"wrap-no-label"}];nfForms.push(form); {{{ data.renderProgressBar() }}} {{{ data.renderBreadcrumbs() }}} {{{ data.renderPartTitle() }}} {{{ data.title }}}
{{{ data.renderNextPrevious() }}} <# if ( data.showPrevious ) { #>
<# _.each( data.parts, function( part, index ) { #>
function registerNinjaFormsCallback() { if (!(window.nfRadio && window.lr_analytics)) { return; } window.nfRadio.channel("forms").on("submit:response", function(submission) { console.log(submission); if (submission.data.form_id !== "48") { return; } window.lr_analytics.track("blog-replay-newsletter-signup", { post: window.location.pathname, email: submission.data.fields_by_key?.email?.value, }); }); } registerNinjaFormsCallback();
Over 200k developers use LogRocket to create better digital experiences
Learn more →
@media all and (max-width: 800px){ .tweet-embed-container {flex-direction: column !important;} .single-tweet, .embed-tweet-right {width: 100% !important;} } .embed-link {text-decoration: none;} .embed-link:hover {text-decoration: none;} .tweet-embed-container {border-radius: 20px; background: radial-gradient(79.69% 102.24% at 100% 100.11%, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0)), radial-gradient(89.7% 115.09% at 3.43% 2.75%, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0)), #764ABC; background-blend-mode: overlay, overlay, normal; box-shadow: 0 4px 0 #d5d5d5; width: auto; padding: 20px 15px; display: flex; flex-direction: row; justify-content: space-evenly; align-items: center; margin: 0 auto; gap: 3%; } .single-tweet {width: 50%;} .single-tweet img {max-width: 100%;height: auto; border-radius:7px;} .embed-tweet-right {width: 46%;} .embed-tweet-right h2 {font-family: 'Avenir'; font-style: normal; font-weight: 500; font-size: 16px; line-height: 28px; color: #FFFFFF;} .embed-btn { display: flex; flex-direction: row; justify-content: left; width: 170px; gap: 5px; align-items: center; padding: 0px 10px; font-family: 'Avenir'; font-style: normal; font-weight: 900; font-size: 16px; line-height: 16px; color: #764ABC; height: 48px; /* White */ background: #FFFFFF; mix-blend-mode: normal; box-shadow: 0px 24px 30px rgba(0, 0, 0, 0.11); border-radius: 80px; border: none; }
Design pattern categories
Let’s take a quick dive into some of the major Javascript design patterns. We will be considering just six (6) patterns in this article:
- Constructor pattern
- Prototype pattern
- Module pattern
- Singleton pattern
- Factory pattern
- Observer pattern
1. Constructor pattern
This is one way to create a constructor pattern:
function Animal (name) {
this.name = name;
this.properties = function() {
console.log(`This ${this.name} can walk`);
}
}
const animalOne = new Animal('Dog');
const animalTwo = new Animal('Cat');
console.log(animalOne.name); //Dog
console.log(animalTwo.name);
animalOne.properties(); //This Dog can walk
animalTwo.properties(); //This Cat can walk
Constructor pattern
For you to access the properties of a function in a constructor pattern, it needs to be initialized. This pattern is useful when thinking about Object Oriented Design.
const object = new ConstructorObject();
The keyword new tells Javascript that ConstructorObject should behave like a constructor. One of the drawbacks of this pattern is that it does not support inheritance. A property shared among different objects will always be repeated.
2. Prototype pattern
In constructor pattern, the method or property set in the object is always redefined when it’s called upon. A better way to resolve this is to create a function inside the prototype function.
With this in place, functions called on instantiation won’t redefine themselves. But a prototype pattern also has a downside. A property is easily shared among all functions even when it’s not needed. You don’t have control over your properties being private or public. It’s automatically public:
function Animal(name) {
this.name = name;
}
Animal.prototype.properties = function() {
console.log(`This ${this.name} can walk`);
};
const animalOne = new Animal('Dog');
const animalTwo = new Animal('Cat');
console.log(animalOne.name); //Dog
console.log(animalTwo.name);
animalOne.properties(); //This Dog can walk
animalTwo.properties(); //This Cat can walk
Prototype pattern
3. Module pattern
Module pattern is a bit of an improvement over prototype pattern. In module pattern, you can set different types of modifiers (both private and public). There is a huge chance one won’t run into conflict in creating the same functions or properties.
You also have the flexibility of re-naming the functions publicly just like we re-named addAnimal function to add. The downside here is the inability to override the created functions from an outside environment. removeAnimal function can’t be overridden from outside without depending on the private property container array:
function AnimalContainter () {
const container = [];
function addAnimal (name) {
container.push(name);
}
function getAllAnimals() {
return container;
}
function removeAnimal(name) {
const index = container.indexOf(name);
if(index < 1) {
throw new Error('Animal not found in container');
}
container.splice(index, 1)
}
return {
add: addAnimal,
get: getAllAnimals,
remove: removeAnimal
}
}
const container = AnimalContainter();
container.add('Hen');
container.add('Goat');
container.add('Sheep');
console.log(container.get()) //Array(3) ["Hen", "Goat", "Sheep"]
container.remove('Sheep')
console.log(container.get()); //Array(2) ["Hen", "Goat"]
Module pattern
4. Singleton pattern
As interesting as the above patterns are, they can’t be used in scenarios where only one instance is needed. Let’s take a look at database connection. You can’t keep on creating an instance of database when it’s already created. You either create a new instance when it’s closed or stop the ongoing instance to create a new one.
Singleton pattern ensures the instance of an object is only created once. It’s also known as the strict pattern. A downside to this pattern is that it is difficult to test. There are hidden dependencies objects, which are difficult to single out in order to test:
function DatabseConnection () {
let databaseInstance = null;
// track number of times the instance is created
let count = 0;
function init() {
console.log(`Opening databse #${count + 1}`);
/**
* perform operation
*/
}
function createIntance() {
if(databaseInstance == null) {
databaseInstance = init();
}
return databaseInstance;
}
function closeIntance() {
console.log('closing database');
databaseInstance = null;
}
return {
open: createIntance,
close: closeIntance
}
}
const database = DatabseConnection();
database.open(); //Opening databse #1
database.open(); //Opening databse #1
database.open(); //Opening databse #1
database.close(); //closing database
Singleton pattern
database.open() from DatabaseConnection object will keep returning 1 because the instance was created only once.
5. Factory pattern
This pattern ensures objects are created with some sort of generic interface. We can specify the type of object we want to create from interface object. Let’s assume we want to handle users payment using multiple vendors(Vendor A, Vendor B … Vendor n). The goal of each vendor is to ensure payment is successfully carried out.
More great articles from LogRocket:
- Don't miss a moment with The Replay, a curated newsletter from LogRocket
- Learn how LogRocket's Galileo AI watches sessions for you and proactively surfaces the highest-impact things you should work on
- Use React's useEffect to optimize your application's performance
- Switch between multiple versions of Node
- Discover how to use the React children prop with TypeScript
- Explore creating a custom mouse cursor with CSS
- Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag
In this kind of scenario, the Factory pattern is our best bet. We won’t have to overthink how payment will be carried out irrespective of the vendor being used at a particular time.
Factory pattern provides an interface where we can specify the type of vendor we want to use in handling payment at each point in time:
/**
* Vendor A
*/
VendorA = {};
VendorA.title = function title() {
return "Vendor A";
};
VendorA.pay = function pay(amount) {
console.log(
`setting up configuration using username: ${this.username} and password: ${
this.password
}`
);
return `Payment for service $${amount} is successful using ${this.title()}`;
};
/**
*Vendor B
*/
VendorB = {};
VendorB.title = function title() {
return "Vendor B";
};
VendorB.pay = function pay(amount) {
console.log(
`setting up configuration using username: ${this.username}
and password: ${this.password}`
);
return `Payment for service $${amount} is successful using ${this.title()}`;
};
/**
*
* @param {*} vendorOption
* @param {*} config
*/
function VendorFactory(vendorOption, config = {}) {
const vendor = Object.create(vendorOption);
Object.assign(vendor, config);
return vendor;
}
const vendorFactory = VendorFactory(VendorA, {
username: "test",
password: "1234"
});
console.log(vendorFactory.title());
console.log(vendorFactory.pay(12));
const vendorFactory2 = VendorFactory(VendorB, {
username: "testTwo",
password: "4321"
});
console.log(vendorFactory2.title());
console.log(vendorFactory2.pay(50));
Factory pattern
Vendor A setting up configuration using username: test and password: 1234 Payment for service $12 is successful using Vendor A
............................................................ Vendor B setting up configuration using username: testTwo and password: 4321 Payment for service $50 is successful using Vendor B
In the factory pattern snippet above, we have two vendors (A and B). The client interfacing with VendorFactory need not to worry about which method to call when switching between vendors.
There is no point in using factory pattern if we don’t really want to create multiple instances of the same object. It would rather make the whole solution more complex.
6. Observer pattern
Observer pattern is useful in cases where an object needs to communicate with some sets of other objects at the same time. Imagine you need to sync an update across many components due to some changes.
Observer pattern prevents unnecessary push and pull of events across states. It notifies the modules involved by modifying the current state of the data:
function Observer() {
this.observerContainer = [];
}
Observer.prototype.subscribe = function (element) {
this.observerContainer.push(element);
}
/**
* removes an element from the container
*/
Observer.prototype.unsubscribe = function (element) {
const elementIndex = this.observerContainer.indexOf(element);
if (elementIndex > -1) {
this.observerContainer.splice(elementIndex, 1);
}
}
/**
* notifies all the element added to the container by calling
* each subscribed components added to the container
*/
Observer.prototype.notifyAll = function (element) {
this.observerContainer.forEach(function (observerElement) {
observerElement(element);
});
}
observer pattern
Let’s take a look at an example to demonstrate observer pattern:
A user types a random number in an input field and the number gets modified and is displayed on two different documents.
This can also be achieved in AngularJS using two-way binding, which makes use of Observer pattern under the hood:
Number multiplied by 2
0
Subscribe UnSubscribeNumber multiplied by 4
0
Subscribe UnSubscribeobserver view
Let’s interact with the elements we created to demonstrate observer pattern.
The observable container (observerContainer) handles how events are stored, retrieved and removed.
A view where the user gets to add a random number which is displayed ondocumentOne(number displayed is multiplied by two) and documentTwo (number displayed is multiplied by four).
Also, there are subscribe and unsubscribe buttons to modify the state of each document to display the modified random number.
The first set of two buttons (subscribe and unsubscribe) updates the display on the documents (<p></p>), the displayed is altered by removing the update operation from the observerContainer by clicking the unsubscribe button.
The same operation is applicable to the next two buttons (subscribe and unsubscribe):
http://observers.js
/**
* get the reference of the views (input, display One and display Two using class name)
* */
const input = document.querySelector('number-input');
const documentOne = document.querySelector('.document-one');
const documentTwo = document.querySelector('.document-two');
/**
* operation to manipulate user input for document one
* */
const updateDocumentOne = function(text) {
documentOne.textContent = parseInt(text) * 2;
}
/**
* operation to manipulate user input for document two
* */
const updateDocumentTwo = function(text) {
documentTwo.textContent = parseInt(text) * 4;
}
const observable = new Observer();
observable.subscribe(updateDocumentOne);
observable.subscribe(updateDocumentTwo);
document.querySelector('.number-input').addEventListener('keyup', function(event){
observable.notifyAll(event.target.value);
});
/**
* modify subscriptions upon a click of a button
* */
document.getElementById('subscribe-one').addEventListener('click', function() {
observable.subscribe(updateDocumentOne);
});
document.getElementById('unsubscribe-one').addEventListener('click', function() {
observable.unsubscribe(updateDocumentOne);
});
document.getElementById('subscribe-two').addEventListener('click', function() {
observable.subscribe(updateDocumentTwo);
});
document.getElementById('unsubscribe-two').addEventListener('click', function() {
observable.unsubscribe(updateDocumentTwo);
});
This is a demo on how we demonstrated the use of observer pattern. The source code can also be found here.

Observer pattern demonstration
If you need to update your view on a simple project, you may want to consider using an observer pattern rather than using a framework.
One of the downsides in observer pattern is difficulty in testing for different scenarios.
Conclusion
Design patterns are highly recommended for Javascript developers. They ensure projects are easily maintained and prevent unnecessary work.
For further reading, I highly recommend Learning Javascript Design Patterns by Addy Osmani.
LogRocket: Debug JavaScript errors more easily by understanding the context
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
.share-icon.share-twitter span:first-child { background-image: url("https://blog.logrocket.com/wp-content/themes/logrocket/assets/svgs/share-twitter.svg"); } .share-icon.share-reddit span:first-child { background-image: url("https://blog.logrocket.com/wp-content/themes/logrocket/assets/svgs/share-reddit.svg"); } .share-icon.share-linkedin span:first-child { background-image: url("https://blog.logrocket.com/wp-content/themes/logrocket/assets/svgs/share-linkedin.svg"); } .share-icon.share-facebook span:first-child { background-image: url("https://blog.logrocket.com/wp-content/themes/logrocket/assets/svgs/share-facebook.svg"); }


Stop guessing about your digital experience with LogRocket
[
Get started for free
](https://lp.logrocket.com/blg/signup)
@media all and (max-width: 750px) { .footer-cta-logo-container {display:none !important;} .footer-cta-container h2 {width: 90% !important; padding-top: 50px !important;} .footer-cta-button {width: 40% !important;} } .footer-cta-container {border-radius: 20px;background: linear-gradient(90.32deg, #8F00FF 0.28%, rgba(143, 0, 255, 0) 99.72%), #764ABC; box-shadow: 0px 64px 74px 0px #764ABC40; padding-bottom: 30px; width: 95%; margin: 0 auto 60px auto; position: relative; margin-top: 3rem; } .footer-cta-tr {position: absolute; top: 30px; right: 30px; } .footer-cta-bl {position: absolute; bottom: 30px; left: 30px; } .footer-cta-bl {} .footer-cta-logo-container { width: 20%; padding: 10px 15px; display:block; margin: 0 auto; position: relative; background: #fafafa; border-radius: 0 0 20px 20px; } .footer-cta-logo-container::before { content: ""; position: absolute; top: 0px; height: 50px; left: -25px; width: 25px; border-top-right-radius: 25px; box-shadow: 0 -25px 0 0 #fafafa; } .footer-cta-logo-container::after { content: ""; position: absolute; top: 0px; height: 50px; right: -25px; width: 25px; border-top-left-radius: 25px; box-shadow: 0 -25px 0 0 #fafafa; } .footer-cta-container h2 {color: #ffffff; text-align: center; width: 70%; position: relative; margin: 40px auto;} .footer-cta-container a {text-decoration: none;} .footer-cta-logo-container img {display: block; margin: 0 auto; padding: 10px; max-width: 150px;} .footer-cta-br {position: absolute; left: 5px; bottom: 5px;} .footer-cta-button {padding: 10px 20px; border: 1px solid #ffffff; width: 20%; border-radius: 20px; color: #ffffff; position: relative; display:block; margin: 0 auto 20px auto; text-align: center; text-decoration: none;}
Recent posts:
[

Should you bet on Valdi instead of React Native?
](https://blog.logrocket.com/valdi-instead-react-native/)
Valdi skips the JavaScript runtime by compiling TypeScript to native views. Learn how it compares to React Native’s new architecture and when the trade-off makes sense.
Dec 30, 2025 ⋅ 7 min read
[

The 8 trends that will define web development in 2026
](https://blog.logrocket.com/8-trends-web-dev-2026/)
What trends will define web development in 2026? Check out the eight most important trends of the year, from AI-first development to TypeScript’s takeover.
Dec 30, 2025 ⋅ 6 min read
[

AI-first debugging: Tools and techniques for faster root cause analysis
](https://blog.logrocket.com/ai-debugging/)
AI-first debugging augments traditional debugging with log clustering, pattern recognition, and faster root cause analysis. Learn where AI helps, where it fails, and how to use it safely in production.
Dec 29, 2025 ⋅ 6 min read
[

Container queries in 2026: Powerful, but not a silver bullet
](https://blog.logrocket.com/container-queries-2026/)
Container queries let components respond to their own layout context instead of the viewport. This article explores how they work and where they fit alongside media queries.
Dec 26, 2025 ⋅ 12 min read
#comments .comment .reply::after { background-image: url("https://blog.logrocket.com/wp-content/themes/logrocket/assets/svgs/reply.svg"); background-repeat: no-repeat; }
Leave a ReplyCancel reply
document.addEventListener('DOMContentLoaded', function () { var commentForms = document.getElementsByClassName('jetpack_remote_comment'); for (var i = 0; i < commentForms.length; i++) { commentForms[i].allowTransparency = false; commentForms[i].scrolling = 'no'; } });
{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/logrocket/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]}
Hey there, want to help make our blog better?
Yea No Thanks
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
var codePrettifyLoaderBaseUrl = "https:\/\/blog.logrocket.com\/wp-content\/plugins\/code-prettify\/prettify"; //# sourceURL=code-prettify-js-before var WPGroHo = {"my_hash":""}; //# sourceURL=wpgroho-js-extra _stq = window._stq || []; _stq.push([ "view", JSON.parse("{\"v\":\"ext\",\"blog\":\"217016018\",\"post\":\"237\",\"tz\":\"-5\",\"srv\":\"blog.logrocket.com\",\"hp\":\"vip\",\"j\":\"1:15.1.1\"}") ]); _stq.push([ "clickTrackerInit", "217016018", "237" ]); //# sourceURL=jetpack-stats-js-before {"baseUrl":"https://s.w.org/images/core/emoji/17.0.2/72x72/","ext":".png","svgUrl":"https://s.w.org/images/core/emoji/17.0.2/svg/","svgExt":".svg","source":{"concatemoji":"https://blog.logrocket.com/wp-includes/js/wp-emoji-release.min.js?ver=6.9"}} /*! This file is auto-generated */ const a=JSON.parse(document.getElementById("wp-emoji-settings").textContent),o=(window._wpemojiSettings=a,"wpEmojiSettingsSupports"),s=["flag","emoji"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(o,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const a=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===a[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e{s[e]=t(o,e,n,a)}),s}function r(e){var t=document.createElement("script");t.src=e,t.defer=!0,document.head.appendChild(t)}a.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(o));if("object"==typeof e&&"number"==typeof e.timestamp&&(new Date).valueOf(){i(n=e.data),r.terminate(),t(n)})}catch(e){}i(n=f(s,u,c,p))}t(n)}).then(e=>{for(const n in e)a.supports[n]=e[n],a.supports.everything=a.supports.everything&&a.supports[n],"flag"!==n&&(a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&a.supports[n]);var t;a.supports.everythingExceptFlag=a.supports.everythingExceptFlag&&!a.supports.flag,a.supports.everything||((t=a.source||{}).concatemoji?r(t.concatemoji):t.wpemoji&&t.twemoji&&(r(t.twemoji),r(t.wpemoji)))}); //# sourceURL=https://blog.logrocket.com/wp-includes/js/wp-emoji-loader.min.js (function () { const iframe = document.getElementById( 'jetpack_remote_comment' ); const watchReply = function() { // Check addComment._Jetpack_moveForm to make sure we don't monkey-patch twice. if ( 'undefined' !== typeof addComment && ! addComment._Jetpack_moveForm ) { // Cache the Core function. addComment._Jetpack_moveForm = addComment.moveForm; const commentParent = document.getElementById( 'comment_parent' ); const cancel = document.getElementById( 'cancel-comment-reply-link' ); function tellFrameNewParent ( commentParentValue ) { const url = new URL( iframe.src ); if ( commentParentValue ) { url.searchParams.set( 'replytocom', commentParentValue ) } else { url.searchParams.delete( 'replytocom' ); } if( iframe.src !== url.href ) { iframe.src = url.href; } }; cancel.addEventListener( 'click', function () { tellFrameNewParent( false ); } ); addComment.moveForm = function ( _, parentId ) { tellFrameNewParent( parentId ); return addComment._Jetpack_moveForm.apply( null, arguments ); }; } } document.addEventListener( 'DOMContentLoaded', watchReply ); // In WP 6.4+, the script is loaded asynchronously, so we need to wait for it to load before we monkey-patch the functions it introduces. document.querySelector('#comment-reply-js')?.addEventListener( 'load', watchReply ); const commentIframes = document.getElementsByClassName('jetpack_remote_comment'); window.addEventListener('message', function(event) { if (event.origin !== 'https://jetpack.wordpress.com') { return; } if (!event?.data?.iframeUniqueId && !event?.data?.height) { return; } const eventDataUniqueId = event.data.iframeUniqueId; // Change height for the matching comment iframe for (let i = 0; i < commentIframes.length; i++) { const iframe = commentIframes[i]; const url = new URL(iframe.src); const iframeUniqueIdParam = url.searchParams.get('iframe_unique_id'); if (iframeUniqueIdParam == event.data.iframeUniqueId) { iframe.style.height = event.data.height + 'px'; return; } } }); })(); {{{ ( 1 == data.settings.show_title ) ? '' + data.settings.title + '' : '' }}}












