Tag: Jetpack

Social Image Generator + Jetpack

I feel like my quest to make sure this site had pretty sweet (and automatically-generated) social media images (e.g. Open Graph) came to a close once I found Social Image Generator.

The trajectory there was that I ended up talking about it far too much on ShopTalk, to the point it became a common topic in our Discord (join via Patreon), Andy Bell pointed me at Daniel Post’s Social Image Generator and I immediately bought and installed it. I heard from Daniel over Twitter, and we ended up having long conversations about the plugin and my desires for it. Ultimately, Daniel helped me code up some custom designs and write logic to create different social media image designs depending on the information it had (for example, if we provide quote text, it uses a special design for that).

As you likely know, Automattic has been an awesome and long time sponsor for this site, and we often promote Jetpack as a part of that (as I’m a heavy user of it, it’s easy to talk about). One of Jetpack’s many features is helping out with social media. (I did a video on how we do it.) So, it occurred to me… maybe this would be a sweet feature for Jetpack. I mentioned it to the Automattic team and they were into the idea of talking to Daniel. I introduced them back in May, and now it’s September and… Jetpack Acquires WordPress Plugin Social Image Generator

“When I initially saw Social Image Generator, the functionality looked like a ideal fit with our existing social media tools,’ said James Grierson, General Manager of Jetpack. ‘I look forward to the future functionality and user experience improvements that will come out of this acquisition. The goal of our social product is to help content creators expand their audience through increased distribution and engagement. Social Image Generator will be a key component of helping us deliver this to our customers.”

Daniel will also be joining Jetpack to continue developing Social Image Generator and integrating it with Jetpack’s social media features.

Rob Pugh

Heck yeah, congrats Daniel. My dream for this thing is that, eventually, we could start building social media images via regular WordPress PHP templates. The trick is that you need something to screenshot them, like Puppeteer or Playwright. An average WordPress install doesn’t have that available, but because Jetpack is fundamentally a service that leverages the great WordPress cloud to do above-and-beyond things, this is in the realm of possibility.

WP Tavern also covered the news:

Automattic is always on the prowl for companies that are doing something interesting in the WordPress ecosystem. The Social Image Generator plugin expertly captured a new niche with an interface that feels like a natural part of WordPress and impressed our chief plugin critic, Justin Tadlock, in a recent review.

“Automattic approached me and let me know they were fans of my plugin,” Post said. “And then we started talking to see what it would be like to work together. We were actually introduced by Chris Coyier from CSS-Tricks, who uses both our products.”

Sarah Gooding

Just had to double-toot my own horn there, you understand.


The post Social Image Generator + Jetpack appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,

Native Search vs. Jetpack Instant Search in Headless WordPress With Gatsby

Have you already tried using WordPress headlessly with Gatsby? If you haven’t, you might check this article around the new Gatsby source plugin for WordPress; gatsby-source-wordpress is the official source plugin introduced in March 2021 as a part of the Gatsby 3 release. It significantly improves the integration with WordPress. Also, the WordPress plugin WPGraphQL providing the GraphQL API is now available via the official WordPress repository.

With stable and maintained tools, developing Gatsby websites powered by WordPress becomes easier and more interesting. I got myself involved in this field, I co-founded (with Alexandra Spalato), and recently launched Gatsby WP Themes — a niche marketplace for developers building WordPress-powered sites with Gatsby. In this article, I would love to share my insights and, in particular, discuss the search functionality.

Search does not come out of the box, but there are many options to consider. I will focus on two distinct possibilities — taking advantage of WordPress native search (WordPress search query) vs. using Jetpack Instant Search.

Getting started

Let’s start by setting up a WordPress-powered Gatsby website. For the sake of simplicity, I will follow the getting started instructions and install the gatsby-starter-wordpress-blog starter.

gatsby new gatsby-wordpress-w-search https://github.com/gatsbyjs/gatsby-starter-wordpress-blog 

This simple, bare-bone starter creates routes exclusively for individual posts and blog pages. But we can keep it that simple here. Let’s imagine that we don’t want to include pages within the search results.

For the moment, I will leave the WordPress source website as it is and pull the content from the starter author’s WordPress demo. If you use your own source, just remember that there are two plugins required on the WordPress end (both available via the plugin repository):

  • WPGraphQL – a plugin that runs a GraphQL server on the WordPress instance
  • WPGatsby – a plugin that modifies the WPGraphQL schema in Gatsby-specific ways (it also adds some mechanism to optimize the build process)

Setting up Apollo Client

With Gatsby, we usually either use the data from queries run on page creation (page queries) or call the useStaticQuery hook. The latter is available in components and does not allow dynamic query parameters; its role is to retrieve GraphQL data at build time. None of those two query solutions works for a user’s-initiated search. Instead, we will ask WordPress to run a search query and send us back the results. Can we send a graphQL search query? Yes! WPGraphQL provides search; you can search posts in WPGraphQL like so:

posts(where: {search: "gallery"}) {   nodes {     id     title     content   } } 

In order to communicate directly with our WPGraphQL API, we will install Apollo Client; it takes care of requesting and caching the data as well as updating our UI components.

yarn add @apollo/client cross-fetch 

To access Apollo Client anywhere in our component tree, we need to wrap our app with ApolloProvider. Gatsby does not expose the App component that wraps around the whole application. Instead, it provides the wrapRootElement API. It’s a part of the Gatsby Browser API and needs to be implemented in the gatsby-browser.js file located at the project’s root.

// gatsby-browser.js import React from "react" import fetch from "cross-fetch" import { ApolloClient, HttpLink, InMemoryCache, ApolloProvider } from "@apollo/client" const cache = new InMemoryCache() const link = new HttpLink({   /* Set the endpoint for your GraphQL server, (same as in gatsby-config.js) */   uri: "https://wpgatsbydemo.wpengine.com/graphql",   /* Use fetch from cross-fetch to provide replacement for server environment */   fetch }) const client = new ApolloClient({   link,   cache, }) export const wrapRootElement = ({ element }) => (   <ApolloProvider client={client}>{element}</ApolloProvider> ) 

SearchForm component

Now that we’ve set up ApolloClient, let’s build our Search component.

touch src/components/search.js src/components/search-form.js src/components/search-results.js src/css/search.css 

The Search component wraps SearchForm and SearchResults

// src/components/search.js import React, { useState } from "react" import SearchForm from "./search-form" import SearchResults from "./search-results"  const Search = () => {   const [searchTerm, setSearchTerm] = useState("")   return (     <div className="search-container">       <SearchForm setSearchTerm={setSearchTerm} />       {searchTerm && <SearchResults searchTerm={searchTerm} />}     </div>   ) } export default Search

<SearchForm /> is a simple form with controlled input and a submit handler that sets the searchTerm state value to the user submission.

// src/components/search-form.js import React, { useState } from "react" const SearchForm = ({ searchTerm, setSearchTerm }) => {   const [value, setValue] = useState(searchTerm)   const handleSubmit = e => {     e.preventDefault()     setSearchTerm(value)   }   return (     <form role="search" onSubmit={handleSubmit}>       <label htmlFor="search">Search blog posts:</label>       <input         id="search"         type="search"         value={value}         onChange={e => setValue(e.target.value)}       />       <button type="submit">Submit</button>     </form>   ) } export default SearchForm

The SearchResults component receives the searchTerm via props, and that’s where we use Apollo Client.

For each searchTerm, we would like to display the matching posts as a list containing the post’s title, excerpt, and a link to this individual post. Our query will be like so:

const GET_RESULTS = gql`   query($  searchTerm: String) {     posts(where: { search: $  searchTerm }) {       edges {         node {           id           uri           title           excerpt         }       }     }   } `

We will use the useQuery hook from @apollo-client to run the GET_RESULTS query with a search variable.

// src/components/search-results.js import React from "react" import { Link } from "gatsby" import { useQuery, gql } from "@apollo/client" const GET_RESULTS = gql`   query($  searchTerm: String) {     posts(where: { search: $  searchTerm }) {       edges {         node {           id           uri           title           excerpt         }       }     }   } ` const SearchResults = ({ searchTerm }) => {   const { data, loading, error } = useQuery(GET_RESULTS, {     variables: { searchTerm }   })   if (loading) return <p>Searching posts for {searchTerm}...</p>   if (error) return <p>Error - {error.message}</p>   return (     <section className="search-results">       <h2>Found {data.posts.edges.length} results for {searchTerm}:</h2>       <ul>         {data.posts.edges.map(el => {           return (             <li key={el.node.id}>               <Link to={el.node.uri}>{el.node.title}</Link>             </li>           )         })}       </ul>     </section>   ) } export default SearchResults 

The useQuery hook returns an object that contains loading, error, and data properties. We can render different UI elements according to the query’s state. As long as loading is truthy, we display <p>Searching posts...</p>. If loading and error are both falsy, the query has completed and we can loop over the data.posts.edges and display the results.

if (loading) return <p>Searching posts...</p> if (error) return <p>Error - {error.message}</p> // else return ( //... )

For the moment, I am adding the <Search /> to the layout component. (I’ll move it somewhere else a little bit later.) Then, with some styling and a visible state variable, I made it feel more like a widget, opening on click and fixed-positioned in the top right corner.

Paginated queries

Without the number of entries specified, the WPGraphQL posts query returns ten first posts; we need to take care of the pagination. WPGraphQL implements the pagination following the Relay Specification for GraphQL Schema Design. I will not go into the details; let’s just note that it is a standardized pattern. Within the Relay specification, in addition to posts.edges (which is a list of { cursor, node } objects), we have access to the posts.pageInfo object that provides:

  • endCursor – cursor of the last item in posts.edges,
  • startCursor – cursor of the first item in posts.edges,
  • hasPreviousPage – boolean for “are there more results available (backward),” and
  • hasNextPage – boolean for “are there more results available (forward).”

We can modify the slice of the data we want to access with the additional query variables:

  • first – the number of returned entries
  • after – the cursor we should start after

How do we deal with pagination queries with Apollo Client? The recommended approach is to use the fetchMore function, that is (together with loading, error and data) a part of the object returned by the useQuery hook.

// src/components/search-results.js import React from "react" import { Link } from "gatsby" import { useQuery, gql } from "@apollo/client" const GET_RESULTS = gql`   query($  searchTerm: String, $  after: String) {     posts(first: 10, after: $  after, where: { search: $  searchTerm }) {       edges {         node {           id           uri           title         }       }       pageInfo {         hasNextPage         endCursor       }     }   } ` const SearchResults = ({ searchTerm }) => {   const { data, loading, error, fetchMore } = useQuery(GET_RESULTS, {     variables: { searchTerm, after: "" },   })   if (loading && !data) return <p>Searching posts for {searchTerm}...</p>   if (error) return <p>Error - {error.message}</p>   const loadMore = () => {     fetchMore({       variables: {         after: data.posts.pageInfo.endCursor,       },       // with notifyOnNetworkStatusChange our component re-renders while a refetch is in flight so that we can mark loading state when waiting for more results (see lines 42, 43)       notifyOnNetworkStatusChange: true,     })   }    return (     <section className="search-results">       {/* as before */}       {data.posts.pageInfo.hasNextPage && (         <button type="button" onClick={loadMore} disabled={loading}>           {loading ? "Loading..." : "More results"}         </button>       )}     </section>   ) } export default SearchResults 

The first argument has its default value but is necessary here to indicate that we are sending a paginated request. Without first, pageInfo.hasNextPage will always be false, no matter the search keyword.

Calling fetchMore fetches the next slice of results but we still need to tell Apollo how it should merge the “fetch more” results with the existing cached data. We specify all the pagination logic in a central location as an option passed to the InMemoryCache constructor (in the gatsby-browser.js file). And guess what? With the Relay specification, we’ve got it covered — Apollo Client provides the relayStylePagination function that does all the magic for us.

// gatsby-browser.js import { ApolloClient, HttpLink, InMemoryCache, ApolloProvider } from "@apollo/client" import { relayStylePagination } from "@apollo/client/utilities" const cache = new InMemoryCache({   typePolicies: {     Query: {       fields: {         posts: relayStylePagination(["where"]),       },     },   }, }) /* as before */ 

Just one important detail: we don’t paginate all posts, but instead the posts that correspond to a specific where condition. Adding ["where"] as an argument to relayStylePagination creates a distinct storage key for different search terms.

Making search persistent

Right now my Search component lives in the Layout component. It’s displayed on every page but gets unmounted every time the route changes. What if we could keep the search results while navigating? We can take advantage of the Gatsby wrapPageElement browser API to set persistent UI elements around pages.

Let’s move <Search /> from the layout component to the wrapPageElement:

// gatsby-browser.js import Search from "./src/components/search" /* as before */ export const wrapPageElement = ({ element }) => {   return <><Search />{element}</> } 

The APIs wrapPageElement and wrapRootElement exist in both the browser and Server-Side Rendering (SSR) APIs. Gatsby recommends that we implement wrapPageElement and wrapRootElement in both gatsby-browser.js and gatsby-ssr.js. Let’s create the gatsby-ssr.js (in the root of the project) and re-export our elements:

// gatsby-ssr.js export { wrapRootElement, wrapPageElement } from "./gatsby-browser" 

I deployed a demo where you can see it in action. You can also find the code in this repo.

The wrapPageElement approach may not be ideal in all cases. Our search widget is “detached” from the layout component. It works well with the position “fixed” like in our working example or within an off-canvas sidebar like in this Gatsby WordPress theme.

But what if you want to have “persistent” search results displayed within a “classic” sidebar? In that case, you could move the searchTerm state from the Search component to a search context provider placed within the wrapRootElement:

// gatsby-browser.js import SearchContextProvider from "./src/search-context" /* as before */ export const wrapRootElement = ({ element }) => (   <ApolloProvider client={client}>     <SearchContextProvider>       {element}     </SearchContextProvider>   </ApolloProvider> ) 

…with the SearchContextProvider defined as below:

// src/search-context.js import React, {createContext, useState} from "react" export const SearchContext = createContext() export const SearchContextProvider = ({ children }) => {   const [searchTerm, setSearchTerm] = useState("")   return (     <SearchContext.Provider value={{ searchTerm, setSearchTerm }}>       {children}     </SearchContext.Provider>   ) }

You can see it in action in another Gatsby WordPress theme:

Note how, since Apollo Client caches the search results, we immediately get them on the route change.

Results from posts and pages

If you checked the theme examples above, you might have noticed how I deal with querying more than just posts. My approach is to replicate the same logic for pages and display results for each post type separately.

Alternatively, you could use the Content Node interface to query nodes of different post types in a single connection:

const GET_RESULTS = gql`   query($  searchTerm: String, $  after: String) {     contentNodes(first: 10, after: $  after, where: { search: $  searchTerm }) {       edges {         node {           id           uri           ... on Page {             title           }           ... on Post {             title             excerpt           }         }       }       pageInfo {         hasNextPage         endCursor       }     }   } `

Our solution seems to work but let’s remember that the underlying mechanism that actually does the search for us is the native WordPress search query. And the WordPress default search function isn’t great. Its problems are limited search fields (in particular, taxonomies are not taken into account), no fuzzy matching, no control over the order of results. Big websites can also suffer from performance issues — there is no prebuilt search index, and the search query is performed directly on the website SQL database.

There are a few WordPress plugins that enhance the default search. Plugins like WP Extended Search add the ability to include selected meta keys and taxonomies in search queries.

The Relevanssi plugin replaces the standard WordPress search with its search engine using the full-text indexing capabilities of the database. Relevanssi deactivates the default search query which breaks the WPGraphQL where: {search : …}. There is some work already done on enabling Relevanssi search through WPGraphQL; the code might not be compatible with the latest WPGraphQL version, but it seems to be a good start for those who opt for Relevanssi search.

In the second part of this article, we’ll take one more possible path and have a closer look at the premium service from Jetpack — an advanced search powered by Elasticsearch. By the way, Jetpack Instant search is the solution adopted by CSS-Tricks.

Using Jetpack Instant Search with Gatsby

Jetpack Search is a per-site premium solution by Jetpack. Once installed and activated, it will take care of building an Elasticsearch index. The search queries no longer hit the SQL database. Instead, the search query requests are sent to the cloud Elasticsearch server, more precisely to:

https://public-api.wordpress.com/rest/v1.3/sites/{your-blog-id}/search 

There are a lot of search parameters to specify within the URL above. In our case, we will add the following:

  • filter[bool][must][0][term][post_type]=post: We only need results that are posts here, simply because our Gatsby website is limited to post. In real-life use, you might need spend some time configuring the boolean queries.
  • size=10 sets the number of returned results (maximum 20).
  • with highlight_fields[0]=title, we get the title string (or a part of it) with the searchTerm within the <mark> tags.
  • highlight_fields[0]=content is the same as below but for the post’s content.

There are three more search parameters depending on the user’s action:

  • query: The search term from the search input, e.g. gallery
  • sort: how the results should be orderer, the default is by score "score_default" (relevance) but there is also "date_asc" (newest) and "date_desc" (oldest)
  • page_handle: something like the “after” cursor for paginated results. We only request 10 results at once, and we will have a “load more” button.

Now, let’s see how a successful response is structured:

{   total: 9,   corrected_query: false,   page_handle: false, // or a string it the total value > 10   results: [     {       _score: 196.51814,       fields: {         date: '2018-11-03 03:55:09',         'title.default': 'Block: Gallery',         'excerpt.default': '',         post_id: 1918,         // we can configure what fields we want to add here with the query search parameters       },       result_type: 'post',       railcar: {/* we will not use this data */},       highlight: {         title: ['Block: <mark>Gallery</mark>'],         content: [           'automatically stretch to the width of your <mark>gallery</mark>. ... A four column <mark>gallery</mark> with a wide width:',           '<mark>Gallery</mark> blocks have two settings: the number of columns, and whether or not images should be cropped',         ],       },     },     /* more results */   ],   suggestions: [], // we will not use suggestions here   aggregations: [], // nor the aggregations }

The results field provides an array containing the database post IDs. To display the search results within a Gatsby site, we need to extract the corresponding post nodes (in particular their uri ) from the Gatsby data layer. My approach is to implement an instant search with asynchronous calls to the rest API and intersect the results with those of the static GraphQL query that returns all post nodes.

Let’s start by building an instant search widget that communicates with the search API. Since this is not specific to Gatsby, let’s see it in action in this Pen:

Here, useDebouncedInstantSearch is a custom hook responsible for fetching the results from the Jetpack Search API. My solution uses the awesome-debounce-promise library that allows us to take some extra care of the fetching mechanism. An instant search responds to the input directly without waiting for an explicit “Go!” from the user. If I’m typing fast, the request may change several times before even the first response arrives. Thus, there might be some unnecessary network bandwidth waste. The awesome-debounce-promise waits a given time interval (say 300ms) before making a call to an API; if there is a new call within this interval, the previous one will never be executed. It also resolves only the last promise returned from the call — this prevents the concurrency issues.

Now, with the search results available, let’s move back to Gatsby and build another custom hook:

import {useStaticQuery, graphql} from "gatsby"  export const useJetpackSearch = (params) => {   const {     allWpPost: { nodes },   } = useStaticQuery(graphql`     query AllPostsQuery {       allWpPost {         nodes {           id           databaseId           uri           title           excerpt         }       }     }   `)   const { error, loading, data } = useDebouncedInstantSearch(params)   return {     error,     loading,     data: {       ...data,       // map the results       results: data.results.map(el => {         // for each result find a node that has the same databaseId as the result field post_id         const node = nodes.find(item => item.databaseId === el.fields.post_id)         return {           // spread the node           ...node,           // keep the highlight info           highlight: el.highlight         }       }),     }   } }

I will call the useJetpackSearch within <SearchResults />. The Gatsby-version of <SearchResults /> is almost identical as that in the Pen above. The differences are highlighted in the code block below. The hook useDebouncedInstantSearch is replaced by useJetpackSearch (that calls the former internally). There is a Gatsby Link that replaces h2 as well as el.fields["title.default"] and el.fields["excerpt.default"] are replaced by el.title and el.excerpt.

const SearchResults = ({ params, setParams }) => {   const { loading, error, data } = useJetpackSearch(params)   const { searchTerm } = params   if (error) {     return <p>Error - {error}</p>   }   return (     <section className="search-results">       {loading ? (         <p className="info">Searching posts .....</p>       ) : (         <>           {data.total !== undefined && (             <p>               Found {data.total} results for{" "}               {data.corrected_query ? (                 <>                   <del>{searchTerm}</del> <span>{data.corrected_query}</span>                 </>               ) : (                 <span>{searchTerm}</span>               )}             </p>           )}         </>       )}       {data.results?.length > 0 && (         <ul>           {data.results.map((el) => {             return (               <li key={el.id}>                 <Link to={el.uri}>                   {el.highlight.title[0]                     ? el.highlight.title.map((item, index) => (                         <React.Fragment key={index}>                           {parse(item)}                         </React.Fragment>                       ))                     : parse(el.title)}                 </Link>                 <div className="post-excerpt">                   {el.highlight.content[0]                     ? el.highlight.content.map((item, index) => (                         <div key={index}>{parse(item)}</div>                       ))                     : parse(el.excerpt)}                 </div>               </li>             );           })}         </ul>       )}       {data.page_handle && (         <button           type="button"           disabled={loading}           onClick={() => setParams({ pageHandle: data.page_handle })}         >           {loading ? "loading..." : "load more"}         </button>       )}     </section>   ) } 

You can find the complete code in this repo and see it in action in this demo. Note that I no longer source WordPress data from the generic WordPress demo used by Gatsby starter. I need to have a website with Jetpack Search activated.

Wrapping up

We’ve just seen two ways of dealing with search in headless WordPress. Besides a few Gatsby-specific technical details (like using Gatsby Browser API), you can implement both discussed approaches within other frameworks. We’ve seen how to make use of the native WordPress search. I guess that it is an acceptable solution in many cases.

But if you need something better, there are better options available. One of them is Jetpack Search. Jetpack Instant Search does a great job on CSS-Tricks and, as we’ve just seen, can work with headless WordPress as well. There are probably other ways of implementing it. You can also go further with the query configuration, the filter functionalities, and how you display the results.


The post Native Search vs. Jetpack Instant Search in Headless WordPress With Gatsby appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

I’ve got one question about Jetpack for you.

And maybe an optional follow-up if you’re up for it.

Automattic, the makers of Jetpack and many other WordPress-y things, have sponsored my site (me = Chris Coyier; site = CSS-Tricks) for quite a while. I use Jetpack myself, and I’m always trying to tell people about its features and benefits.

Yet I get the sense that there is a decent amount of hesitancy (or even general negative feelings) toward Jetpack. I want to hone in on that and understand it better. This will be useful for me in my attempt to be a good sponsoree, and useful for Automattic to improve Jetpack.

Fill out my online form.

The post I’ve got one question about Jetpack for you. appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Jetpack Boost Handles Critical CSS For You

Critical CSS is one of those things I see in my performance reports but always seem to ignore. I know what it means. It means to put only the CSS required to render things immediately visible in a <style> tag in the <head> so it comes across in the first network request, then load the rest asynchronously, which will help the page load faster.

You’ve probably also seen the nag in Lighthouse performance reports as well:

Lighthouse tells me the critical CSS is an opportunity and even gives me a some WordPress-specific tips.

I’m not a big-complex-build-process sort of person, and unfortunately a lot of tooling for critical CSS involves including it within an existing build process.

I caught wind of Jetpack Boost and it’s designed to (among other performance-y things) make critical CSS easy for WordPress sites. Having a (free!) plugin that takes care of that really appeals to me.

Jetpack Boost is freely available in the WordPress Plugin Directory, so it installs just like any other plugin.

Screenshot of the WordPress Plugin Directory in the WordPress admin with an open search for Jetpack Boost, which is the first item in the results out of four total plugins displayed.
Search, install and activate! 🚀

Activating the plugin adds a “Jetpack Boost” item to the WordPress admin menu, and that leads to a handy screen that runs sorta like Lighthouse, but in WordPress. And wouldn’t you know, there’s an option to generate critical CSS right there. All I had to do was toggle the feature on and Jetpack Boost starts working.

The Jetpack Boost admin screen showing the overall performance score of the site, which is a letter B, a mobile score of 83% and desktop score of 86%. Below the scores are three toggle settings to enable critical CSS, deferring non-essential JavaScript, and lazy loading images.
My score can only go up from here, right?!

It doesn’t take long for the process to run. I switched windows to check my email for a minute and it was already done by the time I switched back. And, hey, look what it did for me while I slacked off:

The Jetpack Boost settings screen after critical CSS has been generated. The overall score is now a letter A, with a mobile score of 94, and desktop score of 91.

Not too shabby for clicking one button! But we’ve gotta compare apples to apples, right? Let’s go back into Lighthouse to see what it says.

Lighthouse open in Chrome DevTools. The Performance score is 99 and the opportunity for using critical CSS has passed the audit.
Now if only I could bring that initial server response time down. 🧐

We can even view the source to see that the proof is indeed in the pudding:

DevTools window showing the inline CSS code contained in a style tag in the HTML head. The inlined code is a giant block of white text on a black background.
Whoa, that’s more than I expected!

It’s seriously cool to see the Jetpack team so focused on performance, and to the extent that they’ve dedicated an entire plugin to it. Performance has always been part of Jetpack’s settings. But making it front-and-center like this really allows Jetpack to do more interesting things with performance, like grading and critical CSS, in ways that go beyond basic settings. I’d imagine we’ll see more of Jetpack’s performance settings making the move over to the new plugin at some point.

Kudos to the Jetpack team! This is really nice enhancement, and certainly timely, given Google’s recent push on Core Web Vitals and how they’ll affect SEO.


The post Jetpack Boost Handles Critical CSS For You appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Jetpack Backup: Roll Back Your WooCommerce Site Without Losing Orders

Here’s a dilemma: what happens if your WooCommerce site has a problem and the quickest and best way to fix it is to roll back to a previous version? The dilemma is, if you roll back the database, you would lose literal customer orders that are stored in the database. That’s not acceptable for an eCommerce store.

Good news: Jetpack Backup won’t do you wrong like that. You can still restore to a prior point, but you won’t lose any customer order or product data.

Do you lose all the orders that came in since that last backup? Nope.

Will the inventory get all screwed up? Nope.

What about the new products that were added after the restore point? Still there.

All that data is treated as immutable. The way that it plays out is that the database is restored to that point (along with everything else) and that all the new product and order data that has changed since then is replayed on top of the database after the restore.

With Jetpack Backup, there’s absolutely no guesswork. Its real-time snapshots feature has a unique feature that protects WooCommerce customer and product data when rolling back things back so you get the best-case scenario: readily available backups that preserves customer orders and product information.

That’s just one of the magical benefits you get from such a deep integration between Jetpack and WooCommerce. There are others, of course! But you can imagine just what a big deal this specific integration for any WooCommerce-powered store.

And, hey, Jetpack Backup is sold à la carte. So, if backups are all you want from Jetpack, then you can get just that and that alone.


The post Jetpack Backup: Roll Back Your WooCommerce Site Without Losing Orders appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , , , ,
[Top]

Jetpack Turns 10!

(This is a sponsored post.)

Ten years! That’s a huge milestone for a project, especially one that had a pretty simple goal in mind from the start: give self-hosted WordPress sites many of the same features and functionality enjoyed by hosted WordPress.com sites.

It’s a great story. The Automattic team responsible for driving social activity in WordPress sees Jetpack as a way to expand and unify social activity across all WordPress sites, and winds up paving the way for a product that today helps more than 5 million sites with everything from security and performance to backups and integrations.

And what has Jetpack accomplished in those 10 years and 5 million sites? The numbers are staggering:

  • 122 billion blocked malicious login attempts.
    9,330,623 of those on CSS-Tricks, as we write.
  • 269 million site backups.
    Last backup of CSS-Tricks: 3 minutes ago, as we write.
  • 24 trillion images served by Jetpack CDN.
    Incredibly, a free feature of Jetpack.
  • 61.6 billion site searches.
    Try site search on this site, the latest release has really nice UI & UX improvements, like seeing the post thumbnail.
  • 50 billion related posts displayed. Related posts plugins are notoriously heavy on the server, but not when you let Jetpack do it!
  • 1.6 trillion tracked page views. Those are the numbers at work here with WordPress being some 40% of the web.
  • 2.6 billion shared social posts.
    The @css Twitter account runs itself thanks to Jetpack.

And you know CSS-Tricks is represented in those figures. Jetpack powers our search. We use it for real-time backups and downtime monitoring. It’s what helps us push content to Twitter and display related posts. We use its CDN. We even use a bunch of blocks it includes when we’re writing posts like this. We love Jetpack.

The Jetpack team launched a site celebrating 10 years. And, hey, for a super limited time, you can get 40% off the entire first year of any Jetpack plan.

Direct Link to ArticlePermalink


The post Jetpack Turns 10! appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Jetpack Backup

It’s no secret that CSS-Tricks is a WordPress site. And as such, we like to keep things WordPress-y, like enabling the block editor and creating some custom blocks. We also rely on Jetpack for a number of things, which if you haven’t tried, is definitely worth your time as it’s become a linchpin of this site for everything from search and security scans to social integrations and it’s own set of awesome blocks, like the slideshow block.

But one powerful feature we haven’t talked much about is Jetpack Backup and whoo-boy is it awesome. Sure, code is pretty easy to back up — we’ve got GitHub for that. But what about your assets, like images and other files? Or, gosh, what about the database? These things are super important and losing them would be, well, devastating!

Enter Jetpack Backup. It copies all that stuff, offering two plans, including one for daily backups and the other for real-time backups. Most sites can probably get away with daily backups, but it’s nice to know there’s a real-time option, especially if you’re running an active site, like forums where updates happen regularly, or some eCommerce shop where restoring lost orders would be crucial.

Another thing that makes Jetpack Backup great: it’s sold à la carte. So if backups are all you want from Jetpack, then you can get just that and that alone. But if you need additional features, like all the ones we use around here, then they’re easily accessible and enabled with a few clicks.

You even get a little activity log which is nice to not just see what’s happening on your site, but because it’s another way to pinpoint where things might have gone wrong.

Ugh, Geoff screwing everything up as per usual. 😝

So, yeah, check it out! If you want a deep dive into how it all works, here’s Chris walking through our setup.


The post Jetpack Backup appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Jetpack 8.9: Take Donations, Capture Email Subscribers, AMP integration, and More

(This is a sponsored post.)

Jetpack 8.9 shipped on September 1 and it shows why the plugin continues to be the premier way to take a WordPress site from good to holy smokes! Several new features are packed into the release, but a few really stand out.

Take donations with a new block

The first is donations, and a quick demo of how easy it is to drop a donation form into a page is going to excite anyone who has ever had to cobble together multiple third party scripts and tools to get something like this on a site.

That’s right — it’s as easy as any other block and it connects directly to your Stripe account when you upgrade to a Jetpack paid plan. Non-profits are sure to love this, but even if you’re a plugin developer looking for a way to collect “tips” in exchange for your work, you’ll get a lot of mileage out of something like this.

I’d drop a donations block right here to show you, but if you’re so inclined (😍) we have a MVP supporter thing set up that handles that, which is powered by WooCommerce Memberships.

Collect newsletter signups and automate email marketing

Another feature that stands out is a newsletter signup form. Instead of relying on another plugin for form functionality and another to connect the form to an email newsletter service, Jetpack handles it all with a new block that not only collects subscribers, but integrates directly with Creative Mail by Constant Contact.

That means you not only take signups directly from your site, but you get a way to pull WordPress content and WooCommerce products into emails that support all kinds of automatons, like scheduled sends, action-based triggers, and multi-step marketing journeys. It’s a lot of power in a single package!

It’s worth noting that the newsletter form is in addition to a growing number of forms that are built right into Jetpack, including RSVP, contact, registration, feedback, and appointments.

AMP-ify your content

There isn’t a whole lot of details on this feature, but it certainly warrants attention. Automattic and Google have been working closely together the past several months to ship the 2.0 version of the official AMP plugin for WordPress.

The plan is for the Jetpack team to write up a detailed post sometime soon that thoroughly outlines the integration. Just a guess? Perhaps Jetpack blocks will natively support valid AMP markup in way that maintains the functionality while meeting AMP’s performance standards. We’ll see!


Jetpack 8.9 is the latest release in what’s proving to be a rapidly evolving one-stop shop for the most common and useful WordPress features that normally would require plugin overload. The past year alone has seen a slideshow block, on-site instant search, built-in customer relationship management and security monitoring — and those are just the highlights! You really can’t go wrong with Jetpack if you’re looking for the most powerful set of features in one place. Plug it in, purchase a plan, and you get access to literally dozens of features and enhancements for WordPress without having to hunt them down, one-by-one. Hey, that’s why we use Jetpack around here at CSS-Tricks… and love it!

Direct Link to ArticlePermalink


The post Jetpack 8.9: Take Donations, Capture Email Subscribers, AMP integration, and More appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , , ,
[Top]

Jetpack CRM

About a year ago, Automattic bought up Zero BS CRM. The thinking at the time was that it could be rebranded into the Jetpack suite and, well, that happened.

CRM meaning “Customer Relationship Management” if you’re like me and this is a little outside your sphere of everyday software. CRMs are big business though. When I lived in Silicon Valley, I would regularly drive by a very large building bearing the name of a CRM company that you’ve definitely heard of.

The name is a good one since CRMs are for helping you manage your customers. I have a brother-in-law who sells office furniture. He definitely uses a CRM. He gets leads for new clients, he helps figure out what they need, gets them quotes, and deals with billing them. It sounds straightforward, but it kinda isn’t. CRMs are complicated. Who better to bring you a good CRM than someone who brings you a good CMS?

The fact is that people are different and businesses are different, means that rather than forcing your business to behave like a CRM thinks it should, the CRM should flex to what you need. Jetpack CRM calls it a DIY CRM:

We let entrepreneurs pick and choose only what they need.

What appeals to me about Jetpack CRM is that you can leverage software that you already have and use. I swear the older I get the more cautious I become about adding major new software products to my life. There are always learning curves that are steeper than you want, gotchas you didn’t forsee, and maintenance you end up loathing. The idea of adding major new functionality under the WordPress roof feels much better to me. Everytime I do that with WordPress, I end up thinking it was the right move. I’ve done it now with both forums, newsletters, and eCommerce and each time was great.

I think it’s a smart move for Automattic. People should be able to do just about anything with their website, especially when it has the power of a login system, permission system, database, and all this infrastructure ready to go like any given WordPress site has. A CRM is a necessary thing for a lot of businesses and now Automattic has an answer to that.

Speaking of all the modularity of turning on and off features, you can also select a level that, essentially, specifies how much you want Jetpack CRM to take over your WordPress admin.

If running a CRM is the primary thing you’re doing with your WordPress site, Jetpack CRM can kinda take over and make the admin primarily that with the “CRM Only” setting. Or, just augment the admin and leave everything else as-is with the “Full” setting.

Like most things Jetpack these days, this is an ala-carte purchase if you need features beyond what the free plan offers. It’s not bundled with any other Jetpack stuff.

It’s also like WooCommerce in that there are a bunch of extensions for it that you only have to buy if you need them. For example, the $ 49 Gravity Forms extension allows you to build your lead forms with the Gravity Forms plugin, or it comes bundled in any of the paid plans. The $ 39 MailChimp plugin connects new leads to MailChimp lists.

Mike Stott over on WordPress Tavern:

“There’s a massive opportunity for CRM in the WordPress space,” said Stott. “A CRM is not like installing an SEO plugin on every website you own — generally you’d only have a single CRM for your business — but it’s the core of your business. The fact that 3,000+ users and counting are choosing WordPress to run their CRM is a great start.”


I’m a pretty heavy Jetpack user myself, using almost all it’s features.

Just to be clear, this isn’t some new feature of the core Jetpack plugin that’ll just show up on your site when you upgrade versions. It’s a totally separate product. In fact, the plugin folder you install to is called “zero-bs-crm,” the original name of the product.

I was able to install and play around with it right here on CSS-Tricks with no trouble at all. Being able to create invoices right here on the site is pretty appealing to me, so I’ll be playing with that some more.


The post Jetpack CRM appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

[Top]

Jetpack Scan

Fresh from the Jetpack team at Automattic, today, comes Jetpack Scan. Jetpack Scan scans all the files on your site looking for anything suspicious or malicious and lets you know, or literally fixes it for you with your one-click approval.

This kind of security scanning is very important to me. It’s one of those sleep better at night features, where I know I’m doing all I can do for the safety of my site.

It’s not fun to admit, but I bet in my decade-and-a-half of building WordPress sites, I’ve had half a dozen of them probably have some kind of malicious thing happen. It’s been a long time because I know more, take security way more seriously, and use proper tooling like this to make sure it can’t. But an example is that a malicious actor somehow edits files on your site. One edit to your wp-config.php file could easily take down your site. One edit to your single.php file could put malicious/spammy content on every single blog post. One sketchy plugin can literally do anything to your site. I want to know when any foul play is detected like this.

The new Jetpack.com Dashboard

I’m comforted by the idea that it is Automattic themselves who are checking my site every day and making sure it is clean. Aside from the fact that this is a paid service so they have all that incentive to make sure this does its job, they have the reputation of WordPress itself to uphold here, which is the kind of alignment I like to see in products.

If you’re a user or are familiar with VaultPress, which did backups and security scans, this is an evolution of that. This brings that world into a new dashboard on Jetpack.com (for scans and backup), meaning you can manage all this right from there. Note that this dashboard is for new customers of Jetpack Scan and Backup right now and will soon be available for all existing customers also.

That’s what Jetpack, more broadly, does: it brings powerful abilities of the WordPress.com cloud to your site. For example, backups, CDN hosted assets, instant search, related posts, automatic plugin updates, and more. All of that burden is lifted from your site and done on theirs.

Our page going into the many features of Jetpack we use on this site.

This is also another step toward more à la carte offerings from Jetpack. If you only want this feature and not anything else Jetpack offers, well, you’re in luck. Just like backups, that’s how this feature is sold. Want it? Pay just for it. Don’t want it? Don’t pay for it.

The intro offer (limited time) is $ 7/month or $ 70/year. So getting Jetpack Scan right away is your best value.

Their announcement post is out too. High five gang, very nice release.

The post Jetpack Scan appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]