Preventing Form Spam

Although your site visitors never see it, form spam is a headache for site owners – especially if you get email or text notifications every time a visitor completes a form on your site. Preventing form spam helps alleviate this headache. But what is form spam exactly?

Most form spam occurs when bots discover your form and then attempt to use it as a gateway to submit undesirable information such as abusive language, links to malware or phishing websites, and links to other illegal activities. Once a bot identifies a vulnerability, your form can be used many thousands of times in a short space of time for maliciously. This can result in your domain name getting blacklisted, SMTP services being locked out due to abuse, significant work in resolving and dealing with the situation as well as performance degradation of your website. (So maybe it’s more of a migraine than a headache.)

Regardless of whether your form was coded by hand or created with a WordPress form plugin, it is susceptible to such attacks unless you implement additional strategies to protect it from malicious use.

The best approach is to implement spam prevention measures when the form is originally created to avoid problems later on.

The preventative measures discussed in this article are:

  1. Invisible captchas
  2. Filtering disposable email addresses
  3. Honeypot
  4. WordPress anti-spam plugins

Invisible Captchas

We’re all familiar with the conventional captcha challenge. You check the box to confirm you are a human and then you may have to click additional boxes that contain a certain object such as a traffic signal or motorbike. The captcha is intended to distinguish human from machine input. There are various providers of captcha technologies including the most popular, reCAPTCHA, provided by Google. Another popular captcha with a focus on privacy is hCaptcha.

Captchas provide one of the most efficient ways of preventing form spam, not least because they require a form of human interaction that is incredibly difficult for spam bots to circumvent. A downside, however, is that they can be frustrating for someone completing the form, particularly if the captcha technology does not accept their response and requires subsequent retries, and can be problematic from an accessibility perspective also.

To overcome this problem, some captcha systems now provide invisible versions. The captcha technology sits invisibly on your website and monitors events on your page to determine if a human is present. For example, it may monitor mouse, touch, or key stroke events.

Invisible captchas provide a frictionless way of ensuring a human visitor is submitting forms on your website. Another advantage of invisible captchas over their conventional counterpart is they are more accessible. Clicking on squares containing out of focus images can pose accessibility challenges.

Implementing captchas on a site usually involves client and server side components. The client component is typically a lightweight JavaScript library which will issue a token once it has determined whether or not a human is present. The token is a unique identifier that is then used to retrieve information about the result of the captcha by a server side script.

Let’s use the Google reCAPTCHA system as an example.

The first step is to create reCAPTCHA v3 keys. You can do that by visiting: Two keys are created, a site key (which is a public key) and a secret key which is only used server-side when checking the token.

Once you have your keys you need to add some simple JavaScript to the page containing your form. A simplified example of this script would be:

<!-- Load the Google reCAPTCHA JavaScript library -->

<!-- Submit the form when the reCaptcha is successful -->

    function on_submit(token) {


<!-- Form containing a modified submit button -->
<form id="demo-form">

<button class="g-recaptcha" 


Note that the traditional submit button has been replaced with a custom button element. In this example, when the button is clicked, the reCAPTCHA process will begin and if successful it will call the on_submit function which will submit the form. The form submission POST request will include an additional variable called g-recaptcha-response which contains a token.

The token is then checked server side as part of your PHP script that processes the form POST request. An example script for checking the response is show below:

// Get the reCAPTCHA response from the POST request
$token = $_POST[ 'g-recaptcha-response' ];

// Validate reCAPTCHA response
if( !preg_match( '/^[\w-]*$/', $token ) ) {

	// reCAPTCHA response is invalid

// Make a POST request to verify reCaptcha token
$response = wp_remote_post(



		'body' => array(

			'secret' => 'reCAPTCHA_secret_key',
			'token' => $token

// Check for errors from the wp_remote_post function
if ( is_wp_error( $response ) ) {

	// Handle error
	$error_message = $response->get_error_message();

} else {

	// Decode JSON response
	$response_decoded = json_decode( $response );

	// Check for a successful reCAPTCHA response
	if( !is_null( $response_decoded ) && $response_decoded->success ) {

		// reCAPTCHA was successful, process the form submission

	} else {

		// reCAPTCHA failed, disregard the form submission


Using a form plugin can greatly simplify this process as the client and server components will be handled for you. For example, by using the WS Form form plugin, you simply drag a reCAPTCHA field to your form and configure a few settings to secure your form.

Adding reCAPTCHA V3 to a form using WS Form

Using an invisible captcha on your form is a non-intrusive way of providing a high level of protection against spam bots.

Learn more:

Filtering disposable email addresses

Disposable email addressing, also known as DEA, refers to unique email addresses that are set up for a limited number of uses to hide the identity of the sender. These are often used quite legitimately to protect one’s privacy, but spam bots will often use disposable email addresses when penetrating your forms to look for weaknesses. It is possible you might also get unwanted form submissions that are using disposable email addresses to solicit business or for malicious intent.

You may, therefore, wish to filter disposable email addresses.

Filtering out disposable email addresses can be achieved by checking the submitted email address in the PHP script that processes your form POST request.

First you’re going to need a list of domain names used by disposable email addresses. There are several git repositories containing such lists available online that can be found with a simple Google search. For the purpose of this tutorial we’ll save the list of domains to a file called domains.conf with each domain occupying a separate line in the file. For example:

Next you will create a PHP script to check for those domains.

// Get the email address from the POST request
$email = sanitize_email( $_POST[ 'email' ] );

// Check if the email address is valid
if( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {

	// Split email address by @
	$email_array = explode( '@', $email );

	// Extract domain
	$domain = strtolower( array_pop( $email_array ) );

	// Load blocked domains file into array
	$domains_blocked = file( 'domains.conf', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );

	// Check if domain exists in list of blocked domains
	if( !in_array( $domain, $domains_blocked ) ) {

		// Email address is good, process the form submission
	} else {

		// Disposable email address found, disregard the form submission

Using a form plugin, you can simplify this process. For example, WS Form is integrated with WordPress plugins such as Clearout which provide online services for detecting valid email addresses.

Many form plugins also offer WordPress filter hooks that allow you to add your own filters for detecting disposable email addresses.


Honeypot spam protection involves adding an additional field to your form that is hidden from view to visitors but visible to spam bots. Spam bots will complete the field with a value, whereas genuine visitors will not.

Testing for spam with honeypot is incredibly simply. If a value is present in the field, the submission is rejected. If no value is found, then the submission is accepted.

Honeypot provides an effective method of preventing form spam, and from a user perspective, offers a frictionless experience.

Many form plugins offer an option for enabling Honeypot protection by simply checking a box in the form settings, as shown below for WS Form.

Enabling honeypot spam protection in WS Form

WordPress anti-spam plugins

In addition to the methodologies outlined above, there are a variety of WordPress plugins that can be used to help prevent form spam, such as Akismet, Cleantalk, Jetpack Anti-Spam and WordPress Zero Spam. These plugins integrate with popular form plugins to provide protection for your website.

Check out the WordPress plugin directory to find more!

In conclusion

Form spam doesn’t have to be a headache for the site owner, or cause additional steps for the form completer in order to be avoided. With a little forethought and putting some measures in place, your form can deliver qualified submissions.

Automattic Women: Rebecca Scott

Welcome to Automattic Women—conversations with some of the remarkable women working all over the world to design and develop Automattic software and make the web a better place. Today, software developer Rebecca Scott joins us to discuss technical leadership in the context of Automattic and its unique work culture.

Who are you, and what do you do? 

I’m a software developer by trade, and I’ve moved into leadership roles in the last few years. I’m the Director of Engineering for the Start & Manage Focus group at Woo, where we invest in the activation and management experience for WooCommerce.

My job as I see it is similar to the CTO role for a small startup, where product decisions are being made in different places, often by the CEO (which I equate to our Product Directors). I help the engineering team make technical and tactical decisions, and influence the product direction.

I also assist product directors with technical direction for the product, developing large scale plans and figuring out what resources we need to support projects.

And I work with the team leads within the Start & Manage Focus group to make sure communication protocols are working, refine procedures, and help to solve problems.

Part of this is also acting as a servant leader: I ensure that we’re communicating with other parts of Woo and Automattic, help resolve roadblocks, and work as an interface between the engineering teams and our other stakeholders, such as product and UX design. As a lead of leads, I also mentor and coach the team leads. This gives me a chance to ramble on with my opinions, although I’ve been learning to become a better coach.

What’s a typical day like for you?

I’m not great in the morning, I don’t have a very fixed schedule, so I wake between 6:30 and 8:30, and start work at 9:00 am, after one or two stiff coffees. Most mornings, I don’t have any calls before 9:00 am, although there is the occasional 7:00 am call.

I’ve been using paper lists to organize myself throughout my working life. I’ve tried various apps and techniques, but nothing stuck as much as pen and paper. I start by listing out the morning tasks: Slack, emails, GitHub notifications, and P2s (Automattic’s collaboration platform). Then I copy across anything that didn’t get completed from yesterday.

After writing my list, I start from the top, catching up on notifications: Slack, emails, GH, P2s. That usually takes between one and two hours, but it’s an important part of my job because I use it to be certain I’m talking to the right people, adding comments, and connecting the dots—ensuring I’m across the big picture as much as possible.

Then I launch into different tasks. These might be writing P2s, doing engineering strategic planning, reviewing projects and providing feedback or asking questions, writing HR feedback, working with other teams on feedback or feature requests. It’s a broad range.

Although the bulk of my work is asynchronous, I also have calls booked through the week. I have 1:1s with the team leads and the product director, and I attend team calls such as design reviews when possible. One of my teams is in a different time zone, so it’s rare I can attend their calls, but we’ve started recording important calls so I can review them during my day.

If I happen to get through all my tasks for the day, I have a second list of recurring tasks set up in Trello that I can pick through. These are usually bigger things that I can plug an hour in to and then set aside: longer term planning, lower priority ideas that I can advance a little at a time, working on feedback and improvements to my own processes. I have a professional coach and she often leaves me with “things to think about,” so I try to spend a bit of time self-reflecting. In my position, there isn’t really as much of an opportunity for professional development or passion projects as there is for an IC (individual contributor) here.

Sometimes, if I get through all the critical tasks and only have half an hour left, I don’t feel like I can commit to starting something and actually be able to do anything useful on it, so I’ll just finish a little early. That’s the benefit of our flexible working hours. I know that I’ll be making it up with an early evening call later in the week, so I don’t feel bad about looking after myself. I did 50-60 hour weeks earlier on in my career, and to be honest there’s no real benefit in that hustle. It’s far too easy to burn out and suffer physically and mentally, and you miss out on more important things in life. You don’t win awards for working an 18-24 hour day. I’m glad to work at a place that supports a healthy work/life balance.

What drew you to Automattic and what keeps you here?

I needed a job :-). I had burned out from my last two positions, struggling with my mental health, and had been unemployed for about 6 months. I had used PHP early in my career and it definitely wasn’t my language of choice (that would be C# or Ruby), but I’m practical about languages so switching didn’t bother me too much. Probably the biggest consideration was the remote-first nature of Automattic. I live in a regional area, pretty much in the bush. After working as a consultant in Brisbane for five years, in 2017 I made the decision to move back to my hometown to be close to my family—but that put me in a position where I needed to be able to work remotely.

So that’s what drew me to Automattic. However, once I joined, I started to realize exactly how deep our remote culture goes. Total flexibility, support with setting up a good home office, provided (and regularly upgraded) laptops. And as an IC, I felt well supported by my peers and lead. We could have quite easily been put into a box and just had annual reviews, but instead we have weekly contact to make sure that we’re traveling well, that our future is being thought about, that day to day and week to week frustrations are known and helped with.

I had been a lead before—usually as a CTO or similar in a very small startup—so I knew that that was where I wanted to end up, and when our team restructured, I was able to easily switch to a team lead role. That’s something interesting about Automattic: roles are hats, you don’t get “promoted” to team lead. This can be confusing for people looking for a career path, but essentially, here, you build your own career path and find opportunities to follow it with the support of your lead—which you’re pretty much guaranteed to get.

I loved being a team lead, especially with peer leads, which was something I had never experienced before. Again, I felt like there was a lot of support; it’s not a promotion, but you’re definitely not set up to fail. For example, there are team lead forums and Slack channels, and courses that prepare you for people management.

My career goal is, roughly, technical leadership over a broad product area. I don’t know specifically what job title that would be—job titles aren’t extremely important at Automattic—but when our area’s Director of Engineering (and my former team lead) decided to move on from the company, I had a good talk with him about what the position involved, and decided to give it a try. 

It was probably the best career decision I’ve made! I get to be involved in conversations at all kinds of levels, I  influence the technical and product direction, and it’s really just a lot of fun. I love writing and getting my thoughts out. I have so many opportunities to be heard!

I think we as a company face challenges with building and growing our products (especially our eCommerce products—but I’m biased) in a very competitive market. I’m excited to be part of building a compelling and user-focused eCommerce platform.

What’s your favorite non-work activity?

I’ve always loved playing music. I’m not very good at playing music but it’s a lot of fun. My house has a “kid’s retreat” that I’ve set up as a music room. My kidlette loves playing music (especially drums), so he gets into it too. I mainly play guitar badly and ukulele acceptably.

I recently got a little analog synth (a KORG Monologue) and started messing around with Logic Pro. My plan is to become the next Aphex Twin and be far too famous for Automattic.

That’s it for this edition of Automattic Women. Follow Developer Resources and Automattic Design to meet more wonderful women of Automattic. And if you’d like to do more than just read about these great folks, consider working with us!

Automattic Women: Rossana Menezes

Welcome to Automattic Women—conversations with some of the remarkable women working all over the world to design and develop Automattic software and make the web a better place. Today’s interviewee, Developer Apprenticeship Rossana Menezes, chats with us from her traveling workstation somewhere around the globe.

Who are you, and what do you do?

Automattician Rossana Menezes

At Automattic, I started as a Happiness Engineer in 2017 and am now part of the Developer Apprenticeship Program, an internal pathway into core engineering roles here. I spent one year preparing to apply, working with a mentor, and in January I started as an Android Developer Apprentice working in the WooCommerce mobile team.

The apprenticeship works as a rotation. You are placed in one of our engineering teams across the company and will work full-time on that team for approximately 12 months. The focus of the program is on learning. While you need to have a certain level of programming knowledge to join, the idea is to prepare you to move to a developer role at the end. This has been an amazing experience so far. I am learning so much while also being able to contribute to the WooCommerce mobile app and help our users to manage their stores on the go. 

What’s a typical day like for you?

The best thing about working here is that there is no typical day if that’s what you want. Work-wise, a typical day starts by waking up around 5:00 am, because I am a morning person, and I can work whenever is best for me. So I wake up early, make some good coffee and open my computer to check Slack pings, P2 posts and any notifications on Github. 

Then, I spend about 5 to 10 minutes organizing my day. I use an app called Noteplan 3 that is connected to my Google Calendar, so I can add notes, and have my calendar on the same app. I add a to-do list for that day and as the day goes by and I work on the things I had planned I mark the items on my list as complete, sometimes add more items as they come up. 

Rossana and friends enjoy off-road biking.

With planning out of the way, I start my deep-focus time. It can be either working on a project I am currently assigned to, working on our backlog tickets on Github, reviewing a Pull Request, or focusing on studying something, since learning is a big part of the Apprentice program. I’ve created a blog where I share the things I’m studying.

I started studying programming using Java for a short time and then moved to Kotlin and found it a bit hard to go through its documentation. I felt that everything related to Kotlin was very focused on experienced Java developers, which was definitely not my case. So I  try to post about the things I’m learning using a beginner-friendly approach. 

Depending on the day, I might have a few Zoom meetings too, like 1:1 with my team lead every other Friday, team meeting every two weeks, paired programming sessions with one of my teammates, or group coaching sessions with the other apprentices in my cohort. 

And the best part is being able to do all that from my home office, from the nice coffee house downtown, from the boarding gate while I wait for my flight to Norway to visit my family, or from a super cute restaurant I found while walking around in Carcassonne, France. 

Depending on where I am, my day looks completely different. If I’m home, I usually work from 5:30 am to noon, when I have a break to exercise, eat, and run some errands. Then I go back to work for a couple of hours and work on things that are not necessarily related to coding. So if I need to write a P2 post, work on documentation, or test the app, I do that in the afternoon.

If I am traveling, I usually prefer to work without a long break, so I can finish early and explore the area.

By the sea, by the sea, by the beautiful sea…

What drew you to Automattic and what keeps you here?

What drew me here was the ability to work remotely. I have family in Brazil and Norway—and being able to work while I visit them made a huge difference in my quality of life. I am able to be in Norway for my nieces’ birthdays, spending the holidays with them or with my family in Brazil. I get to travel the world while I work, and this is amazing because traveling is one of my favorite things.

That said, what keeps me here is the company’s culture. Automattic walks the talk. Our creed is not just a bunch of words put together to make the company look nice. It is taken seriously. People here truly care about helping others, creating things that will help our users, and making the web more accessible and a better place in general. And I think because of that, the most amazing people are drawn here.

I work every day with a wonderful group of super smart and kind people who are willing to jump in and help anyone at any time. Also, here you feel that you matter as an employee. We are heard, we are respected, and we are supported and encouraged to grow. You have the freedom and autonomy necessary to balance work and personal life the way it works best for you.

What’s your favorite non-work activity?

Traveling, exploring new places, trying new food, and taking pictures of exciting things. 

That’s it for this edition of Automattic Women. Follow Developer Resources and Automattic Design to meet more wonderful women of Automattic. And if you’d like to do more than just read about these great folks, consider working with us!

Automattic Women: Josepha Haden Chomphosy

Welcome to Automattic Women—conversations with some of the remarkable women working all over the world to design and develop Automattic software and make the web a better place. Today’s interviewee, community steward Josepha Haden Chomphosy, chats with us from her home workstation in California.

“This photo is of my favorite pre-COVID workstations—on the floor at WordCamp.”

Who are you, and what do you do? 

Within Automattic, I lead our open source practice which means my work is a bit of tactics, a lot of strategy, and full-time leadership training and support. Outside of Automattic, I lead the WordPress open source project and that work is a lot of short-term coordination of projects and long-term planning for WordPress’s success.

What’s a typical day like for you?

My days typically start out with Zoom meetings (and lots of coffee) so that I can get as much work unblocked for folks as possible. My afternoons tend to mostly be “desk time,” where I focus on text-based work, especially in the WordPress project and community itself—posts, goals, budgets, decisions—all the back-office things that make the world go ‘round.

“This one is of me running away from a goat that I just petted. I get very excited about goats, otters, and alpacas.”

What drew you to Automattic and what keeps you here?

I was originally drawn to Automattic because working as a sponsored contributor in the WordPress project was a dream come true. I had the chance to take the life-changing experience of discovering WordPress and learning to work with it, and offer that knowledge to others. What keeps me here is naturally the people that I’ve been so privileged to work with over the years, but also the fact that I routinely get to empower others through work that I find meaningful and a CMS that I have used for more than a decade.

What’s your favorite guilty pleasure?

I don’t know how guilty it is, but I spend a lot of time trying to recreate favorite foods from restaurants I’ve been to during my travels. From “best salad” to “best oatmeal,” I’ve tried and failed infinite times on my way to success. But isn’t that the best part of cooking anyway? Taking a bunch of things that are good on their own and combining them until they’re something wholly new and perfect in a totally different way?

That’s it for this edition of Automattic Women. Follow Developer Resources and Automattic Design to meet more wonderful women of Automattic. And if you’d like to do more than just read about these great folks, consider working with us!

WordPress Plugin i18n, Webpack, and Composer

By: Brad Jorsch

A lot of work has been going on in the Jetpack plugin lately. We have UIs built in React, with the JavaScript bundles being created by Webpack. We have Composer packages used for code sharing, increasingly so as we look into creating standalone plugins like Jetpack Backup and Jetpack Search. And we want everything to be translated for people who speak languages other than English.

A few months back we started getting reports that some translations in Jetpack had gone missing. As we looked into it, we eventually found no fewer than six different ways that translation was broken!

  1. JavaScript bundles weren’t being scanned due to bad file naming.
  2. Webpack’s optimizations were breaking the i18n function calls so’s translation infrastructure couldn’t find them.
  3. Lazy-loaded Webpack bundles weren’t lazy-loading translation data.
  4. Shared React component translations didn’t work in plugins other than Jetpack itself.
  5. Bundled Composer packages weren’t being scanned.
  6. Composer package translations didn’t work in plugins other than Jetpack itself.

It took us a few months, but we’ve now fixed them all. This post will describe how we did it.

Background: How plugins get translated

The recommended way to have your plugin translated is to let extract the translatable strings from your plugin, then build language packs for you based on the work of volunteer translators.

In your code, both PHP and JavaScript, you pass translatable strings to functions such as __()_x(), and so on. When that code is uploaded to SVN, it gets scanned for these calls. The strings for each call are collected and passed into the GlotPress installation at, where volunteers translate them into various languages. The translations are later collected into language packs, which can be downloaded and installed into WordPress so people can experience your plugin in their own language.

(Aside: The extraction is part of the WP-CLI tool: wp i18n make-pot. They use the --slug and --ignore-domain options. Generation of JavaScript translation files is done in a manner similar to wp i18n make-json, but skipping any JS files in a src/ directory)

The extracted strings are all associated with a “text domain” matching your plugin’s slug. If the domain parameter passed to __() and so on doesn’t match, your translations won’t be found at runtime.

All this makes some assumptions about your code, some of which turned out not to be true for the way we were doing things in Jetpack.

Problem 1: JavaScript bundle naming

When we dropped support for Internet Explorer 11 in our JavaScript, Babel stopped transpiling modern syntax such as template strings into an ES5 form that IE11 could understand. This then broke when we deployed to, as that environment automatically applies it own minifier to JavaScript and CSS while serving it and their minifier doesn’t understand template strings either. doesn’t apply its minifier if the files are named like “bundle.min.js”, so we renamed our files like that.

But that ran into one of the translation infrastructure’s assumptions: they assume any “bundle.min.js” can be ignored because it will have a corresponding “bundle.js” next to it. 😬

We didn’t want to include several hundred K of non-minified JS in Jetpack though. Jetpack already has an undeserved reputation for being “bloated”, and the extra files wouldn’t help even if the non-minified JS is never used.

Solution: The URL passed to wp_register_script() can include a query part, and’s minifier can also be bypassed by including minify=false in the query part.

Then we took it a step further. The registration of a Webpack bundle usually involves a fair bit of boilerplate since you also need to read the information produced by @wordpress/dependency-extraction-webpack-plugin and often register some CSS too, something like

$relative_to = __FILE__; // Or something.
$assets = require dirname( $relative_to ) . '/build/bundle.asset.php';
    plugin_url( 'build/bundle.js', $relative_to ), // TODO: Add "?minify=false".
wp_set_script_translations( 'handle', 'textdomain' );
    plugin_url( is_rtl() ? 'build/bundle.rtl.css' : 'build/bundle.css', $relative_to ),
    array( /* Some other dependencies? */ ),

So we added a method in our automattic/jetpack-assets Composer package to handle all that in an easier way. And we can have it perform some simple checks, like requiring that a textdomain be given if the dependencies include wp-i18n.

Assets::register_script( 'handle', 'build/bundle.js', __FILE__, array( 'textdomain' => 'domain' ) );

Problem 2: Webpack optimization breaking i18n function calls

The extraction of translatable strings from JavaScript depends on seeing the call to a function or method named __()_x()_n(), or _nx(), with the various parameters being passed as literal strings. These calls may be preceded by a “translator comment” which is also extracted.

In its default configuration, Webpack in production mode is likely to rename these functions to single-character names and to throw away those translator comments. And even if it’s working now, a changed configuration or a new code pattern might break it in the future (as happened to us when we updated to Webpack 5). 😬

Solution, part 1: The first step was to figure out the necessary configuration to preserve the i18n function calls and the translator comments.

  • Set Webpack’s .optimization.concatenateModules false, as the concatenation sometimes winds up renaming the methods.
  • Instead of relying on Webpack’s default configuration for Terser, supply (via .optimization.minimizer) an instance of terser-webpack-plugin configured to preserve the calls and comments.
    • .terserOptions.mangle.reserved set to reserve the four methods.
    • .terserOptions.format.comments set to a callback that identifies translator comments.
    • .extractComments set to a callback that identifies the license comments Terser preserves by default, which will now be extracted to a separate file instead to reduce the size of the bundle.
  • We also included Calypso’s @automattic/babel-plugin-preserve-i18n plugin to further help preserve the i18n method names.

Solution, part 2: To address the “even if it’s working now, it might break later” problem, and to help identify coding patterns that can break the i18n method calls even with the above configuration, we created @automattic/i18n-check-webpack-plugin. This plugin extracts the strings from the original sources and the output bundle to compare them and see if anything seems to have gone missing, so if something breaks it’ll make the build fail instead of having to wait for someone to notice the broken i18n and report it.

The documentation for the check plugin includes some known problematic code patterns and fixes for them.

Problem 3: Lazy-loaded Webpack bundles

For “entry” bundles, Webpack expects your HTML to include any additional files (e.g. CSS extracted by mini-css-extract-plugin) yourself. In a WordPress plugin this is fairly straightforward to do from PHP (and we made it even easier for ourselves using automattic/jetpack-assets as described above), and that includes loading of the appropriate translation data into @wordpress/i18n.

But if you use code like import( /* webpackChunkName: "async" */ './something' ), Webpack will create a “lazy-loaded” bundle that isn’t loaded until that import() call is executed. In Jetpack we have one of these in the Instant Search module. For such lazy-loaded bundles the Webpack runtime knows how to load the extracted CSS, but it knows nothing about WordPress translation data. 😬

When we looked around we saw that Calypso had a fairly complicated solution in their code, a generic hook added in the Webpack runtime and specific code to load data when that hook fired. Woo had tried to adapt that but gave up in favor of tricking WordPress into loading the lazy bundle’s translations non-lazily. Neither solution appealed.

Solution: We created @automattic/i18n-loader-webpack-plugin to teach Webpack how to load the WordPress translation data. It’s designed to work in concert with @wordpress/dependency-extraction-webpack-plugin and automattic/jetpack-assets: when i18n-loader encounters a bundle that can lazy-load other bundles that use @wordpress/i18n, it will register a dependency on a “@wordpress/jp-i18n-state” module via the former that’s provided by the latter. The state data lets the Webpack runtime inside the bundle know how to locate the translation data, which it will then download and register with @wordpress/i18n during the lazy-loading process.

Problem 4: Text domains in shared React components

As part of the “Jetpack RNA” project, we’ve begun creating React components that can be shared by multiple plugins, like our own Jetpack Backup and Jetpack Search plugins.

But remember how the __() call needs to specify a domain, which is supposed to be a constant string (not a variable) and must match the plugin’s slug? 😬

Solution: We created @automattic/babel-plugin-replace-textdomain, a simple Babel plugin to rewrite the domains as the components are being bundled.

Problem 5: Bundled Composer packages being skipped

WordPress core doesn’t really use Composer; they have a composer.json, but just to pull in PHPUnit and a few other development tools. Where WordPress core needs libraries at runtime, they copy them in statically. Plugins either do the same or include Composer’s vendor/ directory in the code checked into SVN.

The translation infrastructure assumes that anything in vendor/ either has no translations or has its own translation mechanism entirely, instead of intending to use WordPress’s. (Although since __() and such would be defined by WordPress rather than the plugin, I’m not sure how that’s intended to work.) In our case, we do really want these packages’ strings included in the plugin’s language pack. 😬

Solution: Composer allows for custom installer plugins, which can install packages into different locations based on the “type” field in the package’s composer.json. We created automattic/jetpack-composer-plugin that installs “jetpack-library” packages into jetpack_vendor/, and set the types of the relevant packages to “jetpack-library”.

Problem 6: Text domains in Composer packages

As with the shared React components, the bundled Composer packages need to be using the plugin’s text domain because that’s where the translations are going to be. And this time we’re not compiling them into a bundle, so a compile-time replacer wouldn’t work. 😬

Solution: The solution here comes in several parts.

  1. We have automattic/jetpack-composer-plugin write an “i18n-map.php” file into jetpack_vendor/, collecting the WordPress plugin’s slug (set in its composer.json) and each package’s textdomain and version (from their composer.jsons).
  2. The WordPress plugin passes that file to automattic/jetpack-assets, which determines the mapping from each package’s domain to an appropriate plugin’s.
  3. Assets hooks into __() and such to try the plugin’s domain if no translation was found for the package’s. It also hooks into the script translation file loader to point to the script translation files included with the plugin’s language pack instead of the nonexistent packs for the packages’ text domains. And finally it includes the mapping in the state data for @automattic/i18n-loader-webpack-plugin so that can load the correct file for any lazy-loaded bundles.

We also made sure that our monorepo’s CI checks would catch common cases where developers might wind up with wrong text domains, using existing linting rules from @wordpress/eslint-plugin and wp-coding-standards/wpcs and custom checks to verify those rules’ configurations are in sync with each other and with composer.json.

If WordPress Core were to take on this problem, I think they could do it a bit better:

  1. Switch the translation infrastructure from being based on plugins and themes (which all use the plugin or theme slug as the text domain) to being based on the text domains directly. For example, instead of and just have one endpoint that takes the domain.
  2. Let code declare to WordPress which domains it needs beyond the defaults of “plugin slug” and “theme slug”. This is so WordPress can download those extra domains, I don’t think WordPress cares beyond that.
  3. Let us register projects that aren’t plugins or themes (e.g. our packages) on, so the packages can be translated and WordPress can fetch those translations like it does plugins and themes.

That way the plugin only needs to declare the packages’ text domains, and the translators would only have to translate each package’s strings once instead of doing so for every plugin using the package.

Summary and conclusion

We created several pieces to make everything work:

We also took advantage of @wordpress/dependency-extraction-webpack-plugin and Calypso’s @automattic/babel-plugin-preserve-i18n, as well as linter rules from @wordpress/eslint-plugin and wp-coding-standards/wpcs (and a fork of phpcs that we’ve been trying to upstream to let us have per-directory configs) to help developers in our monorepo keep text domains straight.

Overall this was quite a bit of work, but Jetpack’s i18n is now better than ever before. And we hope that this post describing the problems we found and our solutions might help other plugin developers improve their i18n as well.

Automattic Women: Anne Mirasol

Welcome to Automattic Women—conversations with some of the remarkable women working all over the world to design and develop Automattic software and make the web a better place. Today’s interviewee, engineer Anne Mirasol, talks to us from her home workstation in the Philippines.

Anne Mirasol at her Automattic workstation.

Who are you, and what do you do? 

I am Anne Mirasol, a software engineer for Automattic. My team is currently working on bringing P2, Automattic’s homegrown remote collaboration platform, to the world.

As a software engineer (or a code wrangler, as we sometimes call it internally), my job is to implement new features, maintain existing ones, and just basically keep P2 running as it should.

What’s a typical day like for you?

I’m a big fan of slow mornings–easing myself into the work day. Even though at Automattic, we work from home, I try to keep my work hours fairly consistent. I find that it personally helps me maintain a healthy work-life boundary.

My work day typically begins in the afternoon, which is also conveniently the time that most of my teammates start their day. We don’t have a lot of face-to-face meetings over Zoom, but we do communicate a lot via Slack and P2.

Whenever I need to step back and do some back-burner work, I go for an ice cream break at the small convenience store near my place. It’s never-ending summer where I am, so every day is a good day for ice cream.

What drew you to Automattic and what keeps you here?

My best friend introduced me to Automattic. She said that it was her dream company, and encouraged me to apply. I valued her opinion a lot, so I was intrigued. It then turned out that the more I learned about the company—by reading all the articles and employee blogs I could find—the more her dream became my dream as well.

I was hesitant to apply at first, since I didn’t know anyone at the company personally, and I thought it just sounded too good to be true. I’m truly thankful for that one Sunday morning where I told myself to stop overthinking, sat up, booted my gaming computer, and just sent in my application. I can say that the company has far exceeded my expectations, and I’m glad to be part of it.

What I like most about Automattic and what makes me stay is the opportunity to work with extremely kind and talented people without having to move to another city or country. It feels like a bigger world has been opened to me, and I didn’t have to leave my family and friends.

A photo I took of my sister and her dog during one of our late afternoon walks around her neighborhood. My sister was telling me to catch up, because the dog doesn’t like leaving anyone behind. “Snowstorm” is our code word for it, and it means I have to walk faster.

What’s your favorite non-work activity?

Before the pandemic, I loved spending the weekend with my twin sister and her dog. She teaches literature at my alma mater, and stays at a housing facility for faculty, so I would regularly spend a night or two with her, and we’d drive around the campus and eat street food, walk the dog, and read paperback fiction while sitting on the university library stairs until after sunset.

Now that I can’t go out as much, I’ve been sampling hobbies and interests and just seeing what holds my interest the longest. To my neighbor’s dismay, I’ve also started learning to play the violin again, working my way through a first-grade book.

That’s it for this edition of Automattic Women. Follow Developer Resources and Automattic Design to meet more great women of Automattic. And if you’d like to do more than just read about these great people, consider working with us!

Automattic Women: Rebecca Williams

Welcome to Automattic Women—conversations with some of the remarkable women working all over the world to design and develop Automattic software and make the web a better place. Today’s interviewee, Rebecca Williams, talks to us from her home on the Wild Atlantic Way coastline of Ireland’s Louisburgh, County Mayo.

Who are you, and what do you do? 

I’m an Engineer Development Wrangler, and I work within Engineer Development in the Developer Experience (DevEx) part of Automattic’s Talent division. Our team supports Automattic engineering teams in the areas of growth, onboarding, and leadership. 

Rebecca Williams in her home office.

My role oversees engineer onboarding and engagement. This involves working with over 150 new starters each year, as well as building and maintaining relationships with leads and other engineers. I am definitely kept busy, but I enjoy every minute of it!

Our process begins at the intersection of hiring and the start date, and we provide support to both our new starters and our leads to ensure that everyone is correctly prepared for the start date. 

As well as individual check-ins, we also hold monthly onboarding calls. These calls are a great opportunity to touch base with our new starters, and to find out what has been working well for them and what we can help with.

Any feedback that we receive during these calls, or via the surveys, is followed-up and acted upon whenever possible to improve the process for future new starters. 

The ultimate goal is to ensure that all of our engineers land smoothly in their teams—that they know what to expect and what is expected of them, and are well placed to begin contributing and feeling productive at the earliest point. We want everyone to feel like they are reaching their full potential, and to consider what else they could achieve within Automattic.

What’s a typical day like for you?

Freedom to choose where we work is one of the great things about jobs at Automattic. Working from home is great, but I also really enjoy working from a coworking space, too. That way I can support small businesses in my local community, and enjoy a change of scenery.

I drop my daughter off at school, then pop into my favorite coffee shop for a caffeine hit. I make my way to the coworking space, or to my home office, depending on where I decide to work that day.

I start my day by checking emails and Slack pings, as well as reading up on any news that happened while I was offline. I try to keep myself organized with productivity tools like Todoist, which I use with Slack integration, enabling me to mark things for attention later.

I start my day by getting all of my operational and process-driven tasks completed, and then I gravitate towards more focused work. At lunch time, I make an effort to take a walk—often to the beach. I find that this is a great way to reset myself and perhaps untangle anything I have been concentrating on in the morning. 

By the time I return to my desk, I am usually ready to go again. I might have some calls, or I might be ready to switch my focus on some other tasks. 

I find time-boxing whatever task I choose to do to be a great way to provide focus for myself. I am definitely a morning person, so by splitting my afternoon, I give myself specific time-frames to work in, which works really well for me. By 3:30, I am ready to pick up my daughter from school. 

When we’re home from school, I log back on and pick things up where I left off until 5:30-6pm. I usually end up feeling pretty productive by the end of the day!

What drew you to Automattic and what keeps you here?

I discovered Automattic at a local remote-workers’ meet-up run by Grow Remote. Afterwards, I read as many blogs by Automatticians as I could find, and one in particular really spoke to me. Having previously worked in a great team, I decided that this emotional connection with others was a really important factor for me, wherever I landed.

The entire Creed resonated with me, but particularly “I will never pass up the opportunity to help out a colleague, and I’ll remember the days before I knew everything.” This is still incredibly important to me, and something that I see for myself being demonstrated on a daily basis. In many ways, onboarding embodies this statement perfectly!

Snapshot from one of Rebecca’s favorite walks. “It was a magical evening when I took that shot; everything was just right!

Another thing that was really important to me was finding an organization that was family-friendly. Being able to do the school run, and be present for my daughter is crucial. Additionally, being able to move my working day around to accommodate dental, physiotherapy appointments, and school visits means that I can spend my annual leave with my family, and really get the benefit from that offline time.

This role provides me the best of everything: a great and supportive team, awesome colleagues, work that keeps me interested and curious—and being able to do all of this from my home. What’s not to love?!

What’s your favorite non-work activity?

Two things really recharge me: spending time in nature, and focusing on a project. When the weather is kind, I will take a long walk—there’s no shortage of interesting routes to take and I have some favourites! 

When the weather is not great, I love to cross-stitch: I immerse myself in really complex projects that are intricate and time consuming. My last stitching project was Cinderella’s Castle, and that took four years to complete (with a Master’s degree in the middle of that!). My current project is An Evening in Venice. It’s taken two years so far, and I’m not even halfway through!

That’s it for this edition of Automattic Women. Follow Developer Resources and Automattic Design to meet more great women of Automattic. And if you’d like to do more than just read about these great people, consider working with us!

Automatically Deploy to Using GitHub Actions provides you with a great experience out-of-the-box, from superb SEO to social media sharing, custom web fonts, and so much more.

At, we also cover very advanced needs, such as connecting GitHub to sites on a Business or eCommerce plan. This allows you to automate deployment using GitHub Actions, which can become a huge productivity boost for developers. You’ll be happy to know that GitHub and work beautifully together.

Prerequisite: Start your site now

Tutorial Overview, GitHub Deployment Automation

This is a tutorial that explains how to achieve deep customization of your site using SFTP access, including how to deploy custom themes and plugins. Moreover, we’ll explain how you can automate the deployment of those changes by simply connecting your GitHub repository to Whenever you push changes to a specific branch of your GitHub repository, those changes will automatically be deployed to your site at as well.

Create SFTP Credentials at

At, create SFTP Credentials for your site. SFTP access is available to administrators on the Pro plan. SFTP creds settings page

Log In Over SFTP, Manually Review Directory Structure

In the screenshot below, which was taken from Filezilla (a popular SFTP client), you can see the primary extensibility features in WordPress, i.e., themes and plugins. Themes are in /wp-content/themes; plugins are in /wp-content/plugins.

If you recreate this structure in a Git repository starting from the /wp-content directory, then whenever you push changes to the GitHub repository, they will be deployed automatically to your site at

Some core directories and files have a question mark (?) icon next to them. This denotes they are part of your site’s core installation. We do not allow modifying core files as they are required for your site to function properly.

Filezilla SFTP client example screenshot

Create GitHub Repository

I’ve created an example GitHub repository named: wp-github-actions-test-site. Inside this repository, I’ve added a custom theme and a custom plugin that I’ll deploy automatically as part of this tutorial. I have already pushed these changes to the main branch of my example repository. Using my example (screenshot below), please create a GitHub repository for your site at

Creating GitHub repo example screenshot

Create New Branch:

Create a new branch with the name, such that you have two branches: main and Whenever you push changes to the branch, you want those changes to be deployed automatically to You can create the new branch using GitHub’s interface, as seen below, or from the command line. The choice is yours.

GitHub repo branch structure example screenshot

Create GitHub Action

On the main branch, create a workflow by copying and pasting the code snippet (provided below). Please copy & paste the snippet with no modifications into .github/workflows/

Note: It is easiest to use GitHub’s interface for this, as shown here.

Creating GitHub Action workflow example screenshot
GitHub Actions WordPress automation example workflow script screenshot Workflow Code Snippet

Copy & paste into .github/workflows/

name: Deploy to
    branches: [ ]
    name: FTP-Deploy-Action
    runs-on: ubuntu-latest
      - uses: actions/checkout@master
          fetch-depth: 2
      - name: FTP-Deploy-Action
        uses: Automattic/FTP-Deploy-Action@3.0.1
          ftp-server: s
          ftp-username: ${{ secrets.FTP_USER }}
          ftp-password: ${{ secrets.FTP_PASSWORD }}
          known-hosts: "\
   ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB\

Create GitHub Secrets

The workflow code snippet references two secrets:

- ${{ secrets.FTP_USER }}
- ${{ secrets.FTP_PASSWORD }}

Create these two secrets in your GitHub repository using the SFTP credentials that you obtained at in an earlier part of this tutorial. Please define both the FTP_USER and FTP_PASSWORD secrets.

GitHub Actions workflow secret example screenshot
GitHub Actions secrets settings in workflow

Push Changes, Test Deployment Automation

Now that you have everything configured properly, update your local copy of the GitHub repository using Git from the command line. Start by fetching all updates, then pull changes into the main branch, merge them into the branch, and finally push those changes to the branch. In doing so, you will test the GitHub Action workflow to make sure things are working properly.

$ git fetch --all
$ git checkout main && git pull
$ git checkout && git merge main
$ git push --set-upstream origin

At this point, you can visit the Actions tab at GitHub to review and observe status. Since you pushed to the branch, the workflow you created should already be running. When it’s finished, the status icon will turn green with a checkmark indicating your changes were deployed successfully to

GitHub Actions workflow ran success screen within repository

In this case, the workflow is deploying your last change, which was to “Create the” file itself — from a previous step in this tutorial.

In the future, when you merge and then push any changes, such as theme or plugin updates, from the main branch into the branch, they’ll be deployed to automatically. Yay! 🎉

The Pro Plan at

At, you’ll be well-equipped without installing custom themes or plugins. However, if you want to take your site to the next level, our Pro plan supports several additional features. This plan includes increased storage, custom themes, custom plugins, and SFTP access, making the GitHub integration described in this tutorial possible.

Start your site now

The Rise of WordPress Frontend Options, Themes vs. REST API + JS/React: Decoupling WordPress

The opportunity to move to a decoupled or headless WordPress system is an emerging discussion because of the broad range of possibilities it opens for developers with complex architecture needs. Decoupling, or separating the backend from the frontend with a framework like Strattic, also delivers a level of complexity that should be factored in when planning a project’s development. Certain projects can benefit from decoupling, while the fully integrated WordPress platform better serves most use cases.

When the words “Learn JavaScript Deeply” were spoken by Matt Mullenweg in 2015, the world hadn’t yet fully realized the scope of what would soon be possible. Then, when the WordPress REST API was released as part of core in 2016, the platform made a giant leap into the future. Of course, Gutenberg, the WordPress editor introduced in late 2018, highlighted what could be accomplished with this new feature as it uses the REST API to create and update posts by communicating with the WordPress core backend. Both the REST API and a modern JS framework built on React are shipped with every copy of WordPress and are now integral components.

This evolution of WordPress means that today, developers are no longer limited to PHP. Instead, developers can create apps built on React and other JavaScript frameworks, served by Node, and powered by the WordPress REST API. This enables WordPress development by frontend developers who don’t write PHP, as well as integration between WordPress and other frontend technologies and systems. 

Key Issues To Consider 

First, a reminder of some key issues worth seriously considering as you weigh decoupling, versus using WordPress as a complete solution. 

  • Performance – Many developers want to decouple in the belief that this increases performance or scale. This, however, rarely proves itself in reality.  Decoupled front-ends must be scaled separately from the WordPress backend. A good host largely solves scaling in traditional architectures. A top-tier enterprise WordPress platform, such as WordPress VIP, will also scale endpoints for decoupled architectures and can serve Node apps at the same scale as anything else being served.  However, decoupled front-ends present their own points of failure, their own caching mechanisms, and generally increase performance risk.  For example, if the site is down, is the front-end causing the problem?  If so, which part of it?  Or is it the API?  
  • Total Cost of Ownership (TCO) – Even teams with mature JS capacity typically find that decoupled experiences have higher TCO than traditional ones. This is primarily due to duplicate hosting, multiple technologies, split teams, multiple codebases, multiple caching strategies, and more. The overall cost of support and build of a decoupled project is almost always higher than a traditional integrated WordPress build. It is essential to consider cost in terms of the whole lifecycle of the project.
  • Solved problems / ecosystem – Out-of-the-box WordPress architectures have more access to integrations and community solutions than custom front-ends.  Sure, React supports many community extensions and integrations, but when we limit the scope to the kinds of extensions required for content apps, WordPress’ extensions and integrations are almost always easier and cheaper to figure out. Examples include AMP and other delivery channels, authentication/SSO, many platform integrations, and even features such as “preview” become an issue that needs to be solved anew for decoupled sites.

Available Toolkit

Today’s decoupled WordPress toolkit is quickly growing, and it includes the following popular solutions.

  • GraphQL / WPGraphQLGraphQL is a Query Language built to craft more precise and lightweight queries to bring only the data you need from the REST API into your front-end application. WPGraphQL extends this framework to be specific to WordPress.
  • GatsbyJS – GatsbyJS is a React and GraphQL-powered static site generator built for speed.
  • FrontityFrontity is a React framework for WordPress. Out of the box, it is set up to consume content from the WordPress REST API, giving developers a big head start by providing many of the most common queries already built-in.
  • StratticStrattic is a static site generator and hosting platform tuned for WordPress.

When Decoupling is an Option

Some of the most relevant reasons developers choose a decoupled approach for complex WordPress projects include:

  • Portability – The app or digital experience needs to be extremely portable or agnostic of the back-end technology.
  • Diverse data sources – The app or digital experience pulls data from more than a small handful of sources, with WordPress being only one of those, and where the WordPress source is probably not the most central of those sources.
  • Leveraging an existing JS-oriented dev team – Frequently, teams of JS developers, especially newly minted ones, want to build Node apps and leverage modern front-ends like React. While the current team’s capabilities are worth considering, this reason may or may not be outweighed by other factors. In addition, there is plenty of exciting work for modern JS developers to do within WordPress itself.
  • Moving into the future of modern JS frameworks – Sometimes, teams want to leverage modern frameworks because the technology is newer, supports app-like behaviors natively in the browser, and it is relatively easy to hire qualified modern JS devs.  These are valid considerations, especially if there are other business reasons to invest in this capacity. However, it is crucial to avoid automatically assuming that modern JS does everything better, cheaper, or faster than WordPress’s mix of PHP and modern JS.

AccuWeather: Making Brilliant Use of Decoupling

This marriage of technologies is the result of leveraging the long-term stability of WordPress with its vast ecosystem with modern frameworks such as React. When used appropriately, this union enables the creation of brilliantly complex applications. One example of this in use is the extremely popular AccuWeather. 

AccuWeather is recognized as the most accurate source of weather forecasts and warnings in the world, serving roughly 1.5 billion people daily. The system comprises weather forecasts, local media partnerships, enterprise solutions, APIs, news, videos, podcasts, and weather data. 

AccuWeather uses a decoupled WordPress stack that includes over 750+ million WordPress requests per day and 50+ billion data calls per day. 

So what advantages were gained by AccuWeather moving to this architecture? 

  1. By retaining the use of WordPress, they can utilize the backend UI/UX experience for the content team. While content is only one part of AccuWeather, it is an ever-growing piece of the brand and company. Using the WordPress backend, the editorial team can work with the most widely adopted and accessible CMS platform, which is used by 40% of the web.
  2. By decoupling the front-end from the back-end, rapid site redesign was made possible without worrying about making massive changes to the platform’s CMS side. 
  3. Finally, the decoupling makes the content easily accessible by other applications. Various development teams, such as app or enterprise teams, can present the content anywhere. For example, they can include a newsfeed with localized content in AccuWeather apps or present targeted video in a widget on a partner site. 

While AccuWeather makes extensive use of the REST API, many other companies and projects can benefit from utilizing and extending the REST API. For example:

  • A vendor may utilize the API for large-scale content migration.
  • A CMS dev team may extend the API to to create mobile apps, or facilitate communication that helps complete 3rd-party integrations.
  • Much like Laravel, WordPress offers developers an extensible REST API framework. A company can easily add new routes and endpoints, giving them a more predictable and structured way to interact with site content or other backend data. They’ll spend less time building tools to access data, and more time creating better user experiences.

One Final Consideration

If all of this seems like the perfect solution for every future project, there’s one final thing to consider before making that jump. 

Decoupling requires a developer or team of developers throughout the entire lifecycle – Developers need to not only be involved in the creation of the project, but will also be needed to manage the site throughout its lifecycle. The client will rarely be able to manage this on their own. While many fully integrated WordPress websites can be left in the care of the site owner, a decoupled WordPress site is usually far too complex for the average site owner to maintain, keep updated, and ensure security and forward compatibility.

Decision Worksheet

Use an internal decision worksheet, such as the one we’ve provided here, to help determine if your project should move forward with a decoupled architecture or not. The more times you answer “No” to the questions, the more caution you should exercise before moving forward. You may still decide that decoupled is the best option, even if you’ve answered ‘No’ to many of the questions, but more deliberation might be needed in that instance.

The Best Choice For You

WordPress leverages the best of a stable, mature product with the promise of evolving with ever-changing modern technological future platforms. Some projects may benefit from the WordPress platform’s integrated approach, while others might require a decoupled architecture. When making decisions, consider the following:

  1. Ask why – Identify the key drivers of your decision, and be honest about this. Are you choosing the more complex decoupling method because it’s the latest cool technology, or is it because it best serves the project?
  2. Consider the total cost of ownership – The TCO over the whole project lifecycle, including multiple hosting, increased complexity, more technical debt, etc., may be significantly higher than you expect.
  3. Look ahead – The landscape around JavaScript and WordPress is changing and expanding rapidly. Can you position your team and your application to thrive in the years and months to come?
  4. Embrace JavaScript – Regardless of your decision to decouple or not, embracing JavaScript will enable you to do exciting things, even within WordPress internally. Learn JavaScript Deeply…still as true today as the day Matt first spoke those words.

Many factors will influence your decision, but WordPress will always be there, either integrated or decoupled, to move your project forward in the best possible way.

Written in collaboration with Donna Cavalier & Jason Caldwell from, and Ryan Sholin & Matt Perry and WordPress VIP.

State of the Word 2020: watch the recording

State of the Word 2020, an annual keynote delivered by WordPress project co-founder Matt Mullenweg, was streamed live on December 17th. Check the recording below to watch at your convenience. You can also watch it on the blog, Facebook, YouTube, or Twitter

Listen first-hand to all the cool improvements that have gone into WordPress this year, thanks to the contributions from thousands of developers and other WordPress enthusiasts from around the world. Also get a glimpse into what’s next for WordPress in 2021. If you develop, design, translate, own a site, or in any way connected with WordPress, then don’t miss it!