Smashing Podcast Episode 38 With Ivan Akulov: Why Is My React App Slow?

In this episode, we’re talking about React performance. What factors slow our React apps down, and how can we fix it? I spoke to expert Ivan Akulov to find out. Show Notes Ivan on Twitter Ivan’s consultancy PerfPerfPerf Smashing Workshop The React Performance Masterclass [SOLD OUT] Weekly Update A Guide To Undoing Mistakes With Git…

How to Create a Firefox Add-on

How to Create a Firefox Add-on – SitePointSkip to main contentFree JavaScript Book!Write powerful, clean and maintainable JavaScript.RRP $11.95 In this tutorial, we’ll go over how to create a Firefox add-on that allows the user to create alarms at specified times with custom text. We’ll cover how to create a basic add-on, adding a popup and an options page, adding permissions, storing information, sending notifications, and creating a background script for sending the notifications.
This tutorial doesn’t require any prior experience with creating Firefox add-ons or any browser extensions. You’ll only need to know some JavaScript basics. You can find the code to this tutorial in this GitHub Repository and you can also find the created add-on published here.
Setting Up Our Firefox Add-on
The first step to create a Firefox add-on is to create the manifest.json file. This file is the only file required for a Firefox add-on. The basic format of a manifest.json file should include the following keys:
name: the name of the add-on in slug format — such as my-extension.
version: the current version of the add-on. When updating anything in the extension, you’ll need to update this version, so it’s recommended to start low.
manifest_version: at the time of writing, Firefox only supports Manifest V2, so the value for this should be 2. However, if future support for V3 is added, the value can be 3 as well.
These’re the mandatory fields for any add-on. The following two are optional but recommended:
description: a short description for your add-on that explains its purpose.
icons: a list of icons of different sizes. These icons will be used in the settings, toolbar of the browser, and other places as well. Recommended sizes to add are 16px, 32px, 48px, and 128px.
For our add-on, let’s start by creating a folder named firefox-alarms-addon. Then add a manifest.json with the following content:
{
“name”: “personalized-alarms”,
“version”: “0.0.1”,
“description”: “Create personalized alarms”,
“manifest_version”: 2,
“icons”: {
“16”: “assets/images/icon16.png”,
“32”: “assets/images/icon32.png”,
“48”: “assets/images/icon48.png”,
“128”: “assets/images/icon128.png”
}
}

As you can see, the icons key is an object with keys of the file size and the path to it. The path is relative to the root of the add-on, which is where manifest.json resides. For this tutorial, I’m using an icon downloaded from iconscout by Twitter Emoji where I can download the different sizes needed as well.
If you’re following along, grab these files from our repo and place them in the appropriate directory (assets/images/).
That’s all that’s needed to create a Firefox add-on!
Loading the Add-on in Firefox
To test our Firefox add-on and be able to debug it later on before uploading it to Mozilla’s Developer Hub, open Firefox, then choose Add-ons and Themes from the right menu, or using the shortcut ctrl + shift + A. Then, Click on the “Settings” icon next to Manage Your Extensions and choose Debug Add-ons.

A new page’ll open for Temporary Extensions.

Click on Load Temporary Add-on button and choose the manifest.json file you just created. If everything was done correctly, you’ll see the newly created add-on with some information about it and the icon we specified in the manifest.json.

Firefox add-ons can be made accessible via different methods, and one of them is by adding a popup page. When adding a popup page, the icon for your extension will show up in the toolbar and once the user clicks on it, the popup page you specify will show up.
We’ll use the popup page to show the user the list of upcoming alarms and a link to add a new alarm that takes the user to the options page (which we’ll talk about in the next section).
Create a popup.html file in the project root with the following content:
DOCTYPE html >

Personalized Alarms

Upcoming Alarms

Add an Alarm

As you can see, it’s just an HTML document. We’ve also added bootstrap.min.css to assets/css and linked it here, and jquery.min.js under assets/js/jquery.min.js and linked it as well. These two libraries are just to make things easier, but you don’t have to actually use them. You can grab them from our repo here and here.
In the content of the page, we’ll show the list of alarms that are upcoming and a link to the options page.
The next step to make a popup work is to add the following in manifest.json:
“browser_action”: {
“default_popup”: “popup.html”,
“browser_style”: true
}

browser_action is an object that has a number of options, but the only mandatory one is default_popup, which is the relative path to the popup from the add-on root directory. browser_style isn’t mandatory, but it’s recommended that it be set to true. This means that Firefox will inject the browser’s default styles to make sure the add-on’s popup styling is similar to the rest of the browser.
That’s all that’s required to add a popup. Go to the Temporary Add-ons page that we went to before and click on the Reload button for the add-on. This will make Firefox check manifest.json for any changes and apply them.
Once you do, you’ll be able to see an icon of your extension in the toolbar menu.

If you click on it, you can see the popup page we just created.

Two things are still left in our popup to make it fully functional: using storage to get the upcoming alarms, and making the “Add an Alarm” link take the user to the options page.
Using Storage
Storage in browser extensions allows us to store data relevant to the extension or the user, either locally on the machine, or in sync based on their account. Local storage stores information locally in the browser, which means that if the user is logged in to Firefox with the same email from another machine, this stored information will not be present there. Sync storage stores information for the current user logged, which allows this information to be available wherever the user is logged in.
Sync storage should be used for certain settings the user wants to have available everywhere, whereas local storage should be used for information or options that are relevant for the current browser installation only.
In our example, we’ll make alarms available everywhere the user is logged in, so we’ll store them in sync storage. But let’s say we want to add a “temporary disable” option that mutes the alarms for a while. In that case it would probably be more suitable to use local storage.
Storage can be accessed easily through the Storage API through get and set methods, but first, we need to request permission to use storage in our add-on. This can be done inside manifest.json:
“permissions”: [
“storage”
],

When the user installs your add-on, they’ll get to see what permissions you require and need them to accept to install your add-on.
There’s one other thing we need to add in order to be able to test the add-on locally: an explicit add-on ID to be able to use the storage. To do so, add this in the manifest.json as well:
“browser_specific_settings”: {
“gecko”: {
“id”: “addon@example.com”,
“strict_min_version”: “42.0”
}
}

This is just to be able to test it locally. Once we publish it, we’ll remove this from the manifest.
The next thing we’ll do is create a new assets/js/popup.js file, which will get the alarms from storage and display them.
To get items from the storage, you can use browser.storage.sync.get or browser.storage.local.get. This depends on whether you’re storing the information in sync storage or local storage. In our case, we’re storing alarms in sync storage, so we’ll use browser.storage.sync.get. It should be noted that all methods under browser.storage.sync.* and browser.storage.local.* have the same signature and accept/return the same types.
browser.storage.sync.get takes one parameter: an array of strings that are the keys of the data we’re retrieving. These keys are defined when we set the storage (which we’ll talk about in the next section). This function returns a promise that resolves to a results object containing the keys we specified in the first parameters and their values, if they exist.
Note: if you’re making the add-on compatible with Chrome, be sure to check out the “Making Add-ons Compatible with Chrome” section.
Create assets/js/popup.js with the following content:
$(document).ready(() = > {
const listElement = $(‘#alarmsList’);

browser.storage.sync.get([‘alarms’])
.then((result) = > {
if (result.alarms && result.alarms.length) {

result.alarms.forEach((alarm) = > {
appendItem(alarm.content, alarm.time);
});
} else {

appendItem(‘No alarms are available’);
}
});

function appendItem(content, badgeContent = null) {
listElement.append(`

${content}
${badgeContent ? `${badgeContent}` : ”}

`);
}
});

You’ll also need to include this file in popup.html:

When the document is ready, we’re using browser.storage.sync.get to get the alarms created by the user. We’re then checking if there are any alarms. If there are, we’re looping over them and displaying them using the appendItem helper function, which just appends an HTML list element li to #alarmsList. If there are no alarms available, we’re just showing “no items available”.
If we reload the add-on now, you’ll notice a new installation of the add-on has been added. This is because we explicitly specified the ID in the manifest.json. You can remove the old one to avoid conflict.
You’ll notice that nothing has changed in our popup, since we don’t have any alarms added yet. We’ll do this in the next section.
Adding an Options Page
To allow your users to customize or edit options or settings in the add-on, you create an HTML page that holds the options and the logic behind setting or changing them. Then you link to it in the manifest.json file.
In our add-on, we’ll use the Options page to allow the user to create alarms. Let’s first create the file options.html. You can create it anywhere in the add-on project directory. We’ll create it in the root of the project with the following content:
DOCTYPE html >

Options

Add Alarm

Alarm Name

Time

Add a New Alarm

Here, we’re just displaying a form with two input fields: “Alarm Name”, which will be the text displayed in the alarm when the notification is sent, and “Time”, which is the time to set the alarm at.
We’ll need to create assets/js/options.js, which will listen for the submit event for the form and set alarms in the sync storage, adding a new alarm to the array.
Similarly to our use of the get method, to set the storage we can use browser.storage.sync.set or browser.storage.local.set, depending on whether we’re storing the data just locally or in sync between all logged-in instances. Since we’re storing our alarms in sync, we’ll use browser.storage.sync.set.
The set method takes one parameter that’s an object of keys and values. The key is what we use to retrieve the value later on, just like we did earlier with get.
Create assets/js/options.js with the following content:
$(document).ready(() = > {
const nameElm = $(‘#name’);
const timeElm = $(‘#time’);
const formElm = $(‘form’);
formElm.on(‘submit’, () = > {
$(‘.alert’).remove();

browser.storage.sync.get([‘alarms’])
.then((result) = > {
let alarms = result.alarms;
const alarmName = nameElm.val().trim() + ‘_’ + (Math.random() * 100);
if (!alarms) {
alarms = [];
}
alarms.push({
content: nameElm.val().trim(),
time: timeElm.val(),
alarmName
});

browser.storage.sync.set({alarms})
.then(() = > {

formElm.prepend(‘Alarm added successfully’);
nameElm.val(”);
timeElm.val(”);
});
});
return false;
});
});

On form submission, we’re first retrieving stored alarms, if there are any. Then, we’re pushing the new alarm we’re creating through the form to the alarms array. Notice how we’re also creating an alarmName variable. We’ll be using this variable to create a unique alarm, then cancel it when the user deletes it. Finally, we’re using browser.storage.sync.set to set the new alarms array.
You might also notice that we added a TODO comment, which is where we’ll schedule notifications in the next section.
Our options page is now ready. To make it available, we first need to add the following to manifest.json:
“options_ui”: {
“page”: “options.html”,
“browser_style”: false
}

This tells Firefox where to find our Options page. We’re also setting browser_style to false because we don’t want Firefox’s styling to override the Bootstrap styling.
Second, we’ll now make the link in the popup take the user to the options page. To do this, we use the method browser.runtime.openOptionsPage() in a new event listener attached to #optionsLink. We’ll add the following to assets/js/popup.js:
$(document).ready(() = > {

$(‘#optionsLink’).on(‘click’, () = > {
browser.runtime.openOptionsPage();
});

function appendItem(content, badgeContent = null) { … }
});

Now, when the user clicks the “Add an Alarm” link, it will take them to the Options page.
Go to the Temporary Add-ons page, and click the reload button. Now, our options page will be registered.
Let’s test it out. Open the popup and click on “Add an Alarm”. It should take you to the Preferences tab in the add-on’s page, and the content will be the content we added in the options.html page.

Now, try to add a test alarm with any name and time and click on “Add an Alarm”. You should be able to see it in the popup after that.

We still need to make one change to assets/js/popups.js, which is to show alarms whose time is later than the current time. Change the call to browser.storage.sync.get to the following:
browser.storage.sync.get([‘alarms’])
.then((result) = > {
if (result.hasOwnProperty(‘alarms’) && result.alarms) {

const minutes = (new Date).getMinutes().toString().padStart(2, ‘0’);
const hours = (new Date).getHours().toString().padStart(2, ‘0’);
const now = new Date(‘1970-01-01T’ + hours + ‘:’ + minutes + ‘Z’).getTime();

result.alarms.forEach((alarm) = > {
const alarmTime = new Date(‘1970-01-01T’ + alarm.time + ‘Z’).getTime();
if (alarmTime > now) {
appendItem(alarm.content, alarm.time);
}
});
} else {

appendItem(‘No alarms are available’);
}
});

This checks for each alarm if its time is greater than the current time and then displays it. The reason we’re formatting the time as ‘1970-01-01T’ + alarm.time + ‘Z’ is because we’re creating the alarms independent of the date. This is just to make the tutorial simpler. We’re also padding hours and minutes with zeros when they’re one digit when calculating the current time, since the required format for new Date should have two digits for both numbers.
If you check now, you’ll notice that the previous alarm we added is shown or not depending on when its time is. You can also test adding a new alarm at another time to see whether it appears in the popup or not.
Scheduling Notifications
To send notifications, we need to use the Notifications API and the Alarms API. The Alarms API allows us to schedule “alarms” that fire at certain times. Then we can add an event listener for the onAlarm event and dispatch notifications at that time using the Notifications API.
To use the Notifications API and Alarms API, we need to add necessary permissions for each in manifest.json, just like we did before with the Storage API:
“permissions”: [
“storage”,
“alarms”,
“notifications”
],

The next thing we’ll do is replace the TODO we had before with the code necessary to create an alarm inside assets/js/options.js.
To create an alarm, we use the browsers.alarms.create function, to which we pass two parameters. The first one is the name of the alarm. This allows us to have different type of alarms in the add-on and act differently based on the name. The second one is an object of options:
when: the time the alarm should be fired at (in the form of a timestamp)
delayInMinutes: a delay in minutes before the alarm fires, if necessary
periodInMinutes: the number of minutes between each fire
All these options are optional. If you don’t pass any of them, the alarm will be fired once right after creation. If you need to fire the alarm once at a specified time, just pass when with the time to be fired at. If you want to fire the alarm once after a specified number of minutes, you can just pass delayInMinutes. If you want to fire the alarm at a specified number of minutes repeatedly, then you can just pass the periodInMinutes. Unless periodInMinutes is passed, the alarm will fire only once.
In our add-on, we need the alarm to fire once every day at the specified time that the user entered when creating the alarm. So, we’ll use a combination of when and periodInMinutes.
Replace the TODO comment in assets/js/options.js with the following:

const currentDate = new Date();
const currentMonth = (currentDate.getMonth() + 1).toString().padStart(2, ‘0’);
const currentDay = currentDate.getDate().toString().padStart(2, ‘0’);

browser.alarms.create(alarmName, {
when: new Date(
currentDate.getFullYear() + ‘-‘ + currentMonth + ‘-‘ + currentDay + ‘T’ + timeElm.val()
).getTime(),
periodInMinutes: 1440,
});

As a first argument, we’re passing the unique alarm name we created earlier. Alarm names in add-ons should be unique, because if they aren’t the newly added one will override the previous one with the same name. In the options object, we’re passing the time the user chooses in the when property, and for periodInMinutes we’re passing 1440, since that’s the number of minutes in a day.
Just as before, we’re also padding the month and day with 0 if they’re less than one digit to make sure they are two digits, as that’s the required format for new Date.
This means that the alarm will fire at the specified time the user entered once a day.
Now that we’ve created alarms successfully, the next thing we need to do is listen to when these alarms fire, and when they do, send a notification to the user. To do that, we need to use a background script.
Background Scripts
Add-ons, popups, options pages or any other pages are only active when we open them. This means that if we listen to events inside the popup or any other page, the listeners will only work once we open them. This won’t be helpful when listening to alarms at different times of the day.
For this reason, we need a background script. Background scripts are always running in the background, even if the popup, option page, or any other page of the add-on aren’t open. So, inside the background script we can add listeners to any event and ensure they’ll work accordingly.
To add a background script, we first need to add it in manifest.json:
“background”: {
“scripts”: [“assets/js/background.js”]
}

Once we create assets/js/background.js and reload the extension, this script will always be working in the background.
We’ll listen in background.js for the alarms firing. To do that, we need to use browser.alarms.onAlarm.addListener, which takes a function that will execute every time an alarm fires. The function has an alarmInfo object as a parameter, which has information about the fired alarm.
Create assets/js/background.js with the following content:
browser.alarms.onAlarm.addListener((alarmInfo) = > {
const alarmName = alarmInfo.name.split(‘_’)[0];
console.log(alarmName);

});

We’re retrieving the alarm name from alarmInfo by also removing the random integer we attached to it. We’ll then send a notification with the content of alarmName. For now, we’ve just placed a TODO comment. We’ve also added console.log for testing purposes.
Once we reload the extension, this background script will start working and listening for alarms. Let’s test it. Reload the extension, then go to the options page and add an alarm that will go off one minute from now. Next, on the Temporary Add-ons page, click on the Inspect button for the add-on. This will open a new window where you can see the console. If you wait until the time of the alarm, you’ll be able to see the name of the alarm in the console . That’s because we’re currently listening for the alarm and just logging its name in the console.

Now we have a working background script! The next step is to send notifications when an alarm is fired.
Sending Notifications
To create and send a notification, we use the browser.notifications.create method. This method, along with all methods in the Notifications API, is only accessible after adding the notifications permission in manifest.json, which we already added earlier.
browser.notifications.create accepts two arguments:
id: a string to identify the notification. This can be helpful if you’ll later on need to update the notification or clear it. If another notification has the same id, the older one will be replaced by the new one. If this parameter is omitted, an id will be generated.
notificationOptions: an object of options for the notification. This object has three mandatory properties: type, title, message. Based on the type, some other options will be required. The allowed types are basic, which just displays the extension icon, title, and message; image, which shows an image in the notification; list, which shows a list of items, though this mainly only works in macOS; and progress, which shows a progress bar.
At the moment, Firefox only supports the basic type, with the properties type, title, message, and, optionally, iconUrl, specifying the icon to show.
In assets/background.js, we’ll replace the TODO comment with the following:
browser.alarms.onAlarm.addListener((alarmInfo) = > {
const alarmName = alarmInfo.name.split(‘_’)[0];

browser.notifications.create({
type: ‘basic’,
title: alarmName,
message: ‘The alarm you created’
});
});

For the title, we’ll show the message the user entered in the form when creating the alarm, and we’re just adding a descriptive message.
Go back to the Temporary Add-ons page and reload the extension, then test it out. Create the alarm with a close time, and check the notification you receive.

If you don’t receive any notification and you’re using macOS, make sure notifications are allowed from Firefox.
Deleting Notifications
The last feature we’ll add is deleting notifications. We’ll allow the user to delete notifications they see from the popup and cancel the alarms for the deleted notification using the alarm name.
Before we start, we’ll use the trash icon from Feather. You can download it from there, or you can get it from the GitHub repository of this tutorial. It should be added in assets/images/trash.svg.
We need to make changes to assets/js/popup.js to show a trash button next to the time of each alarm. We’ll also use the alarm’s index in the alarms array in the storage as the ID of the element to be able to access it later easily.
We’ll add a new optional parameter for appendItem called id and show a new button:
function appendItem (content, badgeContent = null, id = null) {
listElement.append(`

${content}
${badgeContent ? `

${badgeContent}

` : ”}

`);
}

Then, inside the forEach loop, we’ll add the index to the list of parameters:
result.alarms.forEach((alarm, index) = > {
const alarmTime = new Date(‘1970-01-01T’ + alarm.time + ‘Z’).getTime();
if (alarmTime > now) {
appendItem(alarm.content, alarm.time, index);
}
});

Next, we’ll add a click event listener on .trash-btn that first retrieves the index of the alarm from its parent:
$(‘body’).on(‘click’, ‘.trash-btn’, function () {
const parent = $(this).parents(‘.alarm-item’);
const parentId = parent.attr(‘id’);
const alarmIndex = parentId.split(‘_’)[1];

});

After that, we’ll get the alarms array from the storage, then remove the alarm at index alarmIndex using splice and set the alarms array again in storage:

browser.storage.sync.get([‘alarms’])
.then((result) = > {
let alarms = [];
let alarmName = ”;
if (result.alarms && result.alarms.length > alarmIndex) {
alarmName = result.alarms[alarmIndex].alarmName;
result.alarms.splice(alarmIndex, 1);
}
browser.storage.sync.set({alarms})
.then(() = > {

});
});

Then, we need to cancel the alarm so that it doesn’t ring later on. To do that, we’ll use browser.alarms.clear, which takes the alarm name as a parameter to cancel it. Finally, we’ll remove the alarm element from the popup:

browser.alarms.clear(alarmName);

parent.remove();

And with that, we’ve added a delete feature that deletes an alarm from the storage and also cancels it from going off in the background.
Let’s add some styling to the button we just added. Create the file assets/css/popup.css with the following content:
.trash-btn {
background-color: transparent;
border: none;
}

.trash-btn img {
width: 15px;
height: 15px;
}

Then add this stylesheet in popup.html:

Check the popup now. It should look like this:

Try adding an alarm that should send a notification in the next few minutes. Then remove it. No alarm should sound at the time you scheduled it.
That’s it! We’ve created an extension that stores information for the user in the sync storage, then we learned how to create alarms that fire at certain time intervals. We then created a background script that listens for the alarms firing, and finally we learned how to send notifications to the user after listening to the alarms firing.
The next step in the lifecycle of creating an add-on is publishing it on Mozilla’s Developer Hub.
Publishing the Add-on
Now that we’re ready to publish the add-on, we can remove the browser_specific_settings key in manifest.json, so make sure to do that first.
You need to log in to your account, or create a new one. You can do that here.
Once you’re logged in, you can see the section “My Add-ons”. Click on the Submit a New Add-on button at the bottom right.

The process to submit a new add-on will then start. You’ll first be asked if you’ll publish the add-on on Firefox add-ons Manager or you’ll distribute it on your own. Leave the first default option checked and click Continue.

Next, you’ll be asked to upload the extension. To do that, go to the directory you created the add-on inside and create a compressed ZIP file with all the content. Make sure that the root of the add-on is the root of the ZIP file, which means that manifest.json should be in the root of the ZIP file. Then upload that ZIP file. You can also choose to make the add-on available for Firefox Android.
Note: if you get the error “Duplicate add-on ID found”, make sure that you’ve removed the browser_specific_settings key from manifest.json.
Once the file uploads with no errors, click Continue.
In the next step, you’ll be asked to specify whether your add-on uses any compilers or minifiers or any tool that does any processing to the code of the add-on. The reason behind this is that Firefox will then need you to submit the original code for review. Since our add-on doesn’t use any of these tools, just check No and click Continue.

In the final step, you’ll be asked to enter some information about the add-on. This information will be seen by any user that wants to install your add-on, so make sure to make it as clear and descriptive as possible. Enter the add-on’s name, description, categories, and so on. Once you’re done, click Submit Version. If you’re not ready to fill out some information, don’t worry, as you can edit it later on.
And that’s it! Once you click Submit Version, your add-on will be awaiting review, which doesn’t take long. The review process can take up to a day. Once approved, you’ll get an email notifying you and then you can view the add-on on Firefox store. You can also go to the add-on’s information page and add or edit any information like its description, images, and so on.

Updating the add-on is also easy. You can just upload the updated version and it will be available right away! This makes publishing add-ons on Firefox easier and faster than most other browsers.
Making Add-ons Compatible with Chrome
To make the extension we just created compatible with Chrome, we’ll need to make the following amendments:
Replace all occurrences of browser.* with chrome.*.
On Chrome, all of its APIs use callbacks instead of returning promises. This means that instead of using promises in our code, we need to pass a callback function as the last parameter.
An example of making these changes would be in assets/js/popups.js. We used the following code to get the alarms from storage and display them:
browser.storage.sync.get([‘alarms’])
.then((result) = > {

});

We’ll replace this code with the following:
chrome.storage.sync.get([‘alarms’], (result) = > {

});

That’s it. We just move the rest of the code inside the callback function.
Conclusion
In this tutorial, we went over how to create an add-on with basic and necessary features like using the storage, sending notifications, creating background script, and more. Knowing how to do all of that can help you create add-ons with many features. Start creating something awesome!
Related ArticlesUsing Redis with Node.jsJavaScriptBy

How to Develop WordPress Locally with DevKinsta

How to Develop WordPress Locally with DevKinsta – SitePointSkip to main contentFree JavaScript Book!Write powerful, clean and maintainable JavaScript.RRP $11.95 This article was created in partnership with Kinsta. Thank you for supporting the partners who make SitePoint possible.
Local development is the practice of building, editing, and testing code on a local machine, without the need for internet connectivity.
The benefits are plentiful:
You can work on your own code (or your client’s code) from anywhere.
You can debug plugins and themes within different environments with a few clicks.
You can expedite your development and web design workflow by tweaking things locally as you need and pushing them to a staging environment.

Introducing DevKinsta

DevKinsta is Kinsta’s free suite of tools for local WordPress development that lets you create local instances of WordPress with a complete hosting stack consisting of PHP, Nginx, and MySQL in just a few minutes.
DevKinsta comes with a solid and ever-growing list of features such as:
one-click WordPress site creation
PHP 7.x and 8.x support
local email management
built-in database manager
seamless integration with MyKinsta
a community of WordPress experts
DevKinsta supports macOS, Windows, and Ubuntu/Linux, and is available for free to everyone, not just Kinsta customers.
How to download DevKinsta

DevKinsta is available for macOS, Windows, and Ubuntu/Linux. Here’s how to get started:
Visit kinsta.com/devkinsta and click the Download button.
Add your name and email address in the modal window, then click the button.
The download process will initiate automatically and, based on your operating system, you’ll be downloading either the .dmg, .exe file, or .deb.
Click the file and kick off the installation process.
When you start DevKinsta for the first time, Docker Desktop will be installed as a dependency. DevKinsta uses Docker Desktop for creating containerized WordPress environments.
During the DevKinsta installation process, you may see a popup message that says “Docker Desktop needs privileged access”. If you see that message, click Okay and provide the password for your user account so Docker Desktop can be installed correctly.
After you provide the password for installation, DevKinsta will install Docker Desktop along with some Docker images. The installation can take some time depending on the speed of your internet connection, so feel free to step away from the computer for a while.
System requirements
To successfully install DevKinsta, you’ll need to make sure your local machine meets the following requirements:
at least 5 GB of disk space. 20 GB+ is recommended
at least 1 GB of RAM. 2 GB+ is recommended
a stable internet connection for the download
virtualization enabled in BIOS
For more detailed information, please make sure to go through the recommended installation process in our documentation.
How to Create a Local WordPress Site with DevKinsta
DevKinsta supports three methods for creating local WordPress sites:
New WordPress Site lets you create a local site with the default hosting stack consisting of Nginx, MySQL, PHP 7.4, and the latest version of WordPress.
Import from Kinsta lets you clone a site hosted on Kinsta to your local computer with just a few clicks. After you’re finished with your work, you can even push changes back to a Kinsta staging environment!
Custom Site lets you create a local site with a customized hosting stack. This option allows you to choose your PHP version, specify your database name, and enable WordPress multisite.

Creating a new site with DevKinsa

New WordPress Site
To get started, select the New WordPress Site option. For this site creation method, all you have to do is specify a site name, WordPress admin username, and WordPress admin password. After you’ve filled in these three fields, click Create Site.

Create a new WordPress site in DevKinsta

Import from Kinsta
The second option is to import a site environment that’s already hosted on Kinsta. To do this, click Import from Kinsta and provide your MyKinsta login details.
After logging in, select the Kinsta environment you’d like to clone to your local computer. DevKinsta supports both live and staging environments on Kinsta, so be sure to choose the correct one.
After clicking on an environment, specify whether or not the site is a multisite installation and click Import Site to start cloning your site.

Clone your live site with the Import from Kinsta feature

Custom Site
The third and last option, Custom Site, lets you configure specific settings for your local WordPress installation.
Here are the settings you can tweak with this site creation method:
Site Name
PHP Version (PHP 7.2, 7.3, 7.4, and 8.0)
Database Name
Enable HTTPS
WordPress Site Title
WordPress Admin Email
WordPress Admin Username
WordPress Admin Password
WordPress Multisite Mode

Customize a local WordPress installation with DevKinsta

After configuring your desired settings, click Create Site to start the site creation process.
How to Manage Multiple Sites in DevKinsta
For agencies and developers working on multiple projects at once, DevKinsta lets you deploy and manage multiple local WordPress sites! Every local WordPress site managed by DevKinsta runs in its own containerized environment. This means every site has its own customizable PHP version, WordPress version, email inbox, and more.
To view your DevKinsta site list, click on the site’s icon in the left-hand sidebar.

Deploy multiple WordPress local environments with DevKinsta

On this screen, you can see a list of all your local WordPress sites. To add another site, just click the Add Site button.

Manage multiple WordPress sites with DevKinsta

How to Push Changes to a Kinsta Staging Environment
For users with WordPress sites hosted on Kinsta, DevKinsta makes it easy to push changes online to a Kinsta staging environment. To push a local site to Kinsta, just click the Push to Staging button on your “Site Info” page.

Push your local WordPress site to a Kinsta staging environment

You may be prompted to enter your MyKinsta credentials.
You’ll then need to select a target site to push to. Keep in mind that this process will overwrite the contents of the current staging environment if one exists.

Choose a staging environment to push changes to

Finally, click Push to Staging to confirm the action.

Confirm the Push to Staging action

After pushing your local WordPress site to Kinsta, you can then view the site via the staging environment URL. If necessary, you can then push staging to live in MyKinsta.

DevKinsta ships with a lightweight database management tool called Adminer. Like phpMyAdmin, which we use for sites hosted on Kinsta, Adminer provides you with a web interface to edit database tables, run database queries, import and export backups, and more.
To launch Adminer, click the Database Manager button at the top of the “Site Info” page. Adminer will then open in your default web browser.

Click Database Manager to access Adminer in DevKinsta

After launching Adminer, you’ll see your WordPress database’s tables. The screenshot below shows the database of our “kinstalife” test site. Under the “Table” column, you can see the default WordPress tables like wp_comments, wp_posts, etc.

WordPress database in Adminer

To edit a database entry, click on the desired table. For example, if we want to edit the home and site URL of our WordPress site, we can click on the wp_options table.

Click Select Data to edit your WordPress database tables

On this page, we can edit the option_value for siteurl to update the site URL of our WordPress site, and the same can be done for the home URL as well.

Edit a WordPress database option_value with Adminer

Adminer also supports database import and export as well. This is useful for working with database backup files like the ones we include with downloadable backups.
To import a database file, click Import in the upper left corner of Adminer. Click on Choose Files to select a database backup, and click Execute to start the import process. Adminer supports both raw .sql files as well compressed .sql.gz files.

Import a database backup with Adminer

To export a complete database backup, click Export in the upper left corner of Adminer. Select “gzip” for the output format, “SQL” for the database format, and leave the other settings as they are. Click Export to start the backup process.
Adminer will then export your WordPress database to a compressed .sql.gz file.

Export a database backup from Adminer

Finally, Adminer supports SQL command execution, which means you can run database queries on your WordPress database. For example, if you’re trying to find the amount of autoloaded data in your database, you can run a SQL command below in Adminer.
SELECT SUM(LENGTH(option_value)) as autoload_size FROM wp_options WHERE autoload=’yes’;

To run a database query, click SQL Command in the upper left corner of Adminer. Specify a database query and click Execute to run the command.

Query your database with a SQL command in Adminer

With DevKinsta’s Adminer integration, you have advanced control over your WordPress database.
Whether you need to edit database tables, import or export backups, or run complex SQL commands, DevKinsta has got you covered!

DevKinsta includes a built-in SMTP server and email capture tool. This allows your local WordPress sites to send outgoing emails like a live production site. However, sent emails will be captured and stored in DevKinsta’s email inbox.
This gives you the best of both worlds. You can use DevKinsta to test outgoing email functionality for marketing automation workflows, WooCommerce order confirmations, and more, without spamming the email inboxes of your visitors and customers.
To access DevKinsta’s email inbox, click on the mail icon in the left-hand sidebar.

DevKinsta includes a built-in SMTP server and email capture tool

In the email inbox, you’ll see a list of outgoing emails that were captured. In the screenshot below, you can see an outgoing email from our “kinstalife” test site.

An outgoing email in DevKinsta’s email inbox

To inspect an outgoing email, just click on it. For each email, use DevKinsta to inspect the “from address”, “to address”, body content, time of delivery, and more.

DevKinsta email inbox display modes

You can also choose to display the email in HTML, Plain Text, or Raw mode. The HTML mode is useful for testing out HTML email templates, while the Raw mode lets you inspect email headers like MIME-Version and X-Mailer directly.
Where to Learn More About DevKinsta
To learn more about DevKinsta, be sure to join the official community forum and read the DevKinsta documentation.
We look forward to seeing what you create!