Tag: Jetpack

Jetpack Features We Love and Use at CSS-Tricks

(This is a sponsored post.)

We use and love Jetpack around here. It’s a WordPress plugin that brings a whole suite of functionality to your site ranging from security to marketing with lots of ridiculously useful stuff in between! Here’s our favorite features around here.

Jetpack’s Search feature gives your site an incredibly powerful search engine with the flip of a switch. You get a very fast, truly intelligent search for your entire site that is easily sortable and filterable with literally zero work on your part. You can’t rely on default WordPress search — this is a must-have. Bonus: it’s all handled offsite, so there is minimal burden on your server.

Showing the full-page JetPack search feature.

Backups & Activity

We sleep easy knowing CSS-Tricks is entirely backed up in real-time. Everything is backed up from the site’s content, comments, settings, theme files, media, even WordPress itself.

An activity log shows off everything that happens on the site, and I use that same log to restore the site to any point in time.

Showing a timeline log of site changes in Jetpack Backup.

Performant Media

There are at least four things you have to do with images on websites to make sure you’re serving them in a performance responsible way: (1) use the responsive images syntax to serve an appropriately sized version, (2) optimize the image, (3) lazy load the image, and (4) serve the image from a CDN. Fortunately, WordPress itself helps with #1, which can be tricky. Jetpack helps with the others with the flip of a switch.

And don’t forget about video! VideoPress does even more for your hosted videos. No ads, beautiful feature-rich player, CDN-hosted optimized video, poster graphics for mobile, and you do absolutely nothing different with your workflow: just drag and drop videos into posts.

JetPack Markdown

Writing content in Markdown can be awful handy. Especially on a developer-focused site, it makes sense to offer it to users in the comment section.

With Jetpack Markdown, you also get a Markdown block to use in the block editor so you can write in chunks of Markdown wherever needed.

CSS-Tricks has thousands of pages of content! It’s a challenge for us to surface all the best stuff, particularly on a per-topic basis and without having to hand-pick everything. Showing related posts is tricky to pull off and we love that Jetpack does a great job with it, all without burdening our servers the way other related content solutions can.

Social Connections

We like to tell the world as best as we can when we publish new content. Rather than having to do that manually, we can share the post to Twitter and Facebook the second we hit that “Publish” button. You can always head back to older content and re-publish to social media as well.

Showing the Jetpack publishing settings prior to publishing the post. It shows options to publish to Twitter and Facebook and an area to customize the message.

This isn’t a complete list. The official features page will show you even more. Every site’s needs will be different. There are all sorts of security, design, and promotion features that might be your favorites. If you manage a lot of WordPress sites, as agencies often too, take note there is a new Licensing Portal to manage billing across multiple sites much more easily.

Jetpack Features We Love and Use at CSS-Tricks originally published on CSS-Tricks


, , ,

Jetpack Licensing for Agencies and Professionals

(This is a sponsored post.)

I’ve built WordPress websites for I don’t know how long now, but suffice to say I’ve relied on it for a bulk of the work I do as a freelance front-ender. And in that time, I’ve used Jetpack on a good number of them for things like site backups, real-time monitoring, and security scans, among other awesome things.

I love Jetpack, but darn it if it’s time-consuming to manage those services (plus the licenses!) in multiple accounts that I have to keep track of (and get invoices for!).

The Jetpack team took a major step to make it easier to manage its services with a new licensing plan that’s designed specifically for agencies and individuals like myself who manage a portfolio of clients. Instead of littering my 1Password account with a bunch of different logins for different sites to do something like backing up a site, I can now do it all from the convenience of one account: my own.

Sure sure, you say. Plenty of plugins offer developer licenses that can be used on multiple sites. But how many of them allow you to manage those sites all at once? Not many as far as I know. But Jetpack now has this great big, handy dashboard that gives you a nice overview of all the sites you manage in one place.

Ah, now I can see all the services, how many licenses are assigned to them, and—this is the kicker—one price to pay for the month.

OK, so the new Jetpack agency dashboard does administrative stuff for licensing and payments. But there’s also the ability to manage all of your Jetpack-driven sites from here as well. Tab over to the site manager and you get to work on all those sites at once. Imagine you have to run a site backup each month for 20 sites. Ugh. It’s really one task, but it’s also sort of like doing the same task 20 times. This way, that can all be done together directly from the dashboard—no hopping accounts and repeating the same task over and again.

Jetpack’s agency licensing is a program you sign up for. Once you’re a member, you not only get the dashboard and a single automated bill each month, but a slew of other perks, including a 25% discount on all Jetpack products and early access to new site management features. This is just the first iteration of the dashboard and site manager, and there’s plenty in the pipeline for more goodness to come. Heck, you can even request a feature if you’d like.

There’s just one requirement: you’ve gotta issue at least five licenses within 90 days. That’s fair considering this is an agency sort of thing and agencies are bound to reach that threshold pretty quickly.

This is definitely the sort of thing that excites me because it saves a lot of time on overhead so more time can be spent, you know, working. So, give it a look and see how it can streamline the way you manage Jetpack on multiple sites.


, , ,

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.


, , ,

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:


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.


, , , , , ,

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.


, , , ,

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.


, , ,

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.


, , , , , , , ,

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.



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.



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.


, , , , , , ,