{"id":271524,"date":"2018-05-25T07:42:48","date_gmt":"2018-05-25T14:42:48","guid":{"rendered":"http:\/\/css-tricks.com\/?p=271524"},"modified":"2019-01-02T09:03:26","modified_gmt":"2019-01-02T16:03:26","slug":"learning-gutenberg-7-building-our-block-custom-card-block","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/learning-gutenberg-7-building-our-block-custom-card-block\/","title":{"rendered":"Learning Gutenberg: Building Our Custom Card Block"},"content":{"rendered":"

We\u2019ve got some base knowledge, we\u2019ve played with some React and now we\u2019ve got our project tools set up. Let\u2019s dive into building our custom block.<\/p>\n

<\/p>\n

\n

Article Series:<\/h4>\n
    \n
  1. \n Series Introduction<\/a>\n <\/li>\n
  2. \n What is Gutenberg, Anyway?<\/a>\n <\/li>\n
  3. \n A Primer with create-guten-block<\/a>\n <\/li>\n
  4. \n Modern JavaScript Syntax<\/a>\n <\/li>\n
  5. \n React 101<\/a>\n <\/li>\n
  6. \n Setting up a Custom webpack<\/a>\n <\/li>\n
  7. \n A Custom “Card” Block (This Post)<\/em>\n <\/li>\n<\/ol>\n<\/div>\n

    What we\u2019re building<\/h3>\n

    We\u2019re going to build a custom card block that features an image, a title and a summary. It\u2019s a really common design pattern in the web and it also let\u2019s us look at some core Gutenberg components, along with core WordPress elements, such as the Media Library. We\u2019ll also play with some display logic with JSX for the front-end markup.<\/p>\n

    \"\"
    Our glorious custom card block!<\/figcaption><\/figure>\n

    Our glorious custom card block!<\/p>\n

    We\u2019re going to focus solely on the CMS<\/abbr> aspect of this block in this tutorial. What it renders is some nice, clean markup on the front-end though. You could extend this block to feature front-end styles too, if you wanted.<\/p>\n

    Getting started<\/h3>\n

    The first thing we\u2019re going to do is open up the block.js<\/code> file that we created in the previous section<\/a>. In your active plugin folder, this is located at blocks\/src\/block\/block.js<\/code>.<\/p>\n

    If you are working from the file structure created with create-guten-block, you might want to start by deleting everything in block.js<\/code> and writing your code from scratch along with the tutorial.<\/div>\n

    Right at the top of this file, add the following: <\/p>\n

    const { RichText, MediaUpload, PlainText } = wp.editor;\r\nconst { registerBlockType } = wp.blocks;\r\nconst { Button } = wp.components;<\/code><\/pre>\n

    We covered destructuring assignments in Part 3<\/a>. This is a great example of those, which you\u2019ll see a lot in Gutenberg code. Here, wp.components<\/code> features more than Button<\/code>, but that\u2019s all we want, so that\u2019s all we\u2019ll get. It\u2019s a neat because it prevents us from having to write stuff like wp.components.Button<\/code>, which will be great for keeping our code nice and light.<\/p>\n

    We\u2019ve got that cleared up, so let\u2019s import our Sass files. This is so that webpack detects them.<\/p>\n

    import '.\/style.scss';\r\nimport '.\/editor.scss';<\/code><\/pre>\n

    Now let\u2019s start writing the component that powers our block. Right under those lines, add the following: <\/p>\n

    registerBlockType('card-block\/main', {   \r\n  title: 'Card',\r\n  icon: 'heart',\r\n  category: 'common'\r\n});<\/code><\/pre>\n

    This code tells Gutenberg, “Hey, I\u2019ve got a block for you to add to your collection. It\u2019s called ‘Card,\u2019 it has a ‘heart’ icon and it should live in the ‘common’ category.”<\/em> This is our component\u2019s basic definition, so let\u2019s add some more code. <\/p>\n

    \n

    This should look familiar\u2014remember our challenge in Part 2, create-guten-block<\/a>? In case you need reminding, check it out here. The first six of those were relatively straightforward and involved replacing strings or bit of HTML. The seventh item, \u201cMake the paragraph text editable,” is much<\/em> more complicated to implement and was intended to get you thinking a bit. The time has come though, and we will indeed make some text editable in Gutenberg!<\/p>\n

    You may also recognize this registerBlockType<\/code> function from the PHP register_block_type<\/code> function we used in the last article<\/a>. While that function registers a block from the server-side of WordPress, this one registers our block into the React ecosystem on the client side. Both are necessary to have a block that uses React, and their registered names, card-block\/main<\/code> must match.<\/p>\n<\/div>\n

    Add the following code, but make sure you put a comma after 'common'<\/code>, so it looks like this: 'common',<\/code>. <\/p>\n

    Here\u2019s the code: <\/p>\n

    attributes: {\r\n  title: {\r\n    source: 'text',\r\n    selector: '.card__title'\r\n  },\r\n  body: {\r\n    type: 'array',\r\n    source: 'children',\r\n    selector: '.card__body'\r\n  },\r\n  imageAlt: {\r\n    attribute: 'alt',\r\n    selector: '.card__image'\r\n  },\r\n  imageUrl: {\r\n    attribute: 'src',\r\n    selector: '.card__image'\r\n  }\r\n}<\/code><\/pre>\n

    Here, we are defining the editable attributes of our block and the DOM selector that they are assigned to. This attribute<\/code> object works in a very similar way to the React state<\/code> object. It even has a very similar updating method called setAttributes<\/code>. We\u2019ll get to that later though.<\/p>\n

    At this point, it\u2019s worth a brief overview of state and attributes because they represent a whole new way of thinking for WordPress developers. I\u2019ll take over for a moment to go over them.<\/div>\n

    About Attributes and State<\/h3>\n

    It may look like a simple JavaScript object, but that chunk of attributes<\/code> introduces a whole swath of new concepts to a WordPress theme developer\u2019s brain, not least of which is state<\/strong>. The concept of state has a long history in computer science, and life in general, really. Almost everything has a state. What state is your coffee cup in now? Empty, almost empty? How about your clothing? Are your shoes dirty or new? How about your body? Are you tired or wide awake?<\/p>\n

    At a high level, state<\/em> simply refers to the present condition of a thing<\/strong>. In computer science, that thing<\/em> is a computer program, and that program can be much, much simpler than what we create here on the web. Take a vending machine, for instance. The vending machine has a state that updates each time you put in a coin. When the state of the machine reaches a predefined amount, say $1.25, the machine knows to allow you to make your snack choice. <\/p>\n

    In Gutenberg, attributes track the present condition of data in a block.<\/strong> Attributes are the closest parallel we can draw to custom fields in Gutenberg, but they exist only<\/em> in the context of Gutenberg and JavaScript. Let\u2019s take the attribute above for title<\/code>, for example:<\/p>\n

    title: {\r\n  source: 'text',\r\n  selector: 'card__title'\r\n},<\/code><\/pre>\n

    When Gutenberg fires up, it says, \u201cI need to find some text<\/em> inside a selector<\/em> called .card__title<\/code>, and populate the value for title<\/code> with whatever I find.” <\/p>\n

    Attributes in Gutenberg are not directly connected to the database like custom fields are connected to post_meta<\/code>. The entries source<\/code> and selector<\/code> are instructions for Gutenberg to populate the state<\/strong> of each block. When we load up the editor, it follows these instructions and assigns a value to title<\/code> based on the markup saved in the database between the HTML comments that indicate a block of this type. We don\u2019t see the value of title<\/code> in the attributes<\/code> we register, but if I were to access props.attributes.title<\/code>, I would get whatever text<\/code> exists in .card__title<\/code>.<\/p>\n

    We\u2019ve set up some basics, so let\u2019s dive in to our edit function. This is what\u2019s called when the block is accessed from the Gutenberg editor in visual mode<\/strong>. The user will see the rich interface, rather than the HTML code that it generates. That\u2019s what I\u2019ll cover next.<\/div>\n

    Add our edit function<\/h3>\n

    Let\u2019s add some code in. Add the following after the closing }<\/code> of the attributes<\/code> object. Like before, make sure you add a trailing comma, so it looks like this },<\/code>. <\/p>\n

    Add the following code after that: <\/p>\n

    edit({ attributes, className, setAttributes }) {\r\n  return (\r\n  );\r\n}<\/code><\/pre>\n

    So, we\u2019re using another destructuring assignment to selectively pick our passed parameters to the edit function. The two most important are attributes<\/code> and setAttributes<\/code>. The attributes<\/code> parameter is the same as the attributes<\/code> block, but it\u2019s the current, reactive state. This means if the setAttributes<\/code> function updates one of the attributes<\/code> values, it will automatically update anywhere that references it, which is similar to our React component from Part 3<\/a>.<\/p>\n

    There\u2019s a big ol\u2019 return<\/code> in this function. Can you guess what\u2019s going in it? Yup! We\u2019re going to stick some JSX in there. Add the following within the return<\/code> parentheses:<\/p>\n

    <div className=\"container\">\r\n  <MediaUpload\r\n    onSelect={ media => { setAttributes({ imageAlt: media.alt, imageUrl: media.url }); } }\r\n    type=\"image\"\r\n    value={ attributes.imageID }\r\n    render={ ({ open }) => getImageButton(open) }\r\n  \/>\r\n  <PlainText\r\n    onChange={ content => setAttributes({ title: content }) }\r\n    value={ attributes.title }\r\n    placeholder=\"Your card title\"\r\n    className=\"heading\"\r\n  \/>\r\n  <RichText\r\n    onChange={ content => setAttributes({ body: content }) }\r\n    value={ attributes.body }\r\n    multiline=\"p\"\r\n    placeholder=\"Your card text\"\r\n  \/>\r\n<\/div><\/code><\/pre>\n

    OK, there\u2019s a lot going on in here, but it\u2019s all stuff we\u2019ve covered in previous parts of this series. What we\u2019ve got here is a container with three existing Gutenberg components<\/a>. For each, we are setting the relevant attribute<\/code> as its value, a relevant placeholder and an onChange\/onSelect<\/code> handler. We\u2019re also passing a custom renderer to the <MediaUpload \/><\/code>, which we\u2019ll cover shortly.<\/p>\n

    Each onChange<\/code> handler is a handy little expression that passes the new content that triggered the onChange<\/code> into the setAttributes<\/code> function, where we set which attributes<\/code> object to update. This update then cascades into any reference of that attribute, where the content will update like magic. The <MediaUpload \/><\/code> element features an onSelect<\/code> event which is fired when the user selects or uploads an item to the media library. <\/p>\n

    Speaking of the <MediaUpload \/><\/code> element, you\u2019ll notice there\u2019s a custom render<\/code> attribute, which references a getImageButton<\/code> function. Let\u2019s write that next. Above the return<\/code> in the edit<\/code> function add the following: <\/p>\n

    const getImageButton = (openEvent) => {\r\n  if(attributes.imageUrl) {\r\n    return (\r\n      <img \r\n        src={ attributes.imageUrl }\r\n        onClick={ openEvent }\r\n        className=\"image\"\r\n      \/>\r\n    );\r\n  }\r\n  else {\r\n    return (\r\n      <div className=\"button-container\">\r\n        <Button \r\n          onClick={ openEvent }\r\n          className=\"button button-large\"\r\n        >\r\n          Pick an image\r\n        <\/Button>\r\n      <\/div>\r\n    );\r\n  }\r\n};<\/code><\/pre>\n

    What this function does is detect if there\u2019s an imageUrl<\/code> in the attributes<\/code> object. If there is, it\u2019ll render that <img \/><\/code> tag and let a user click it to select another. If there\u2019s no image, it\u2019ll render a WordPress <Button \/><\/code> which prompts the user to pick an image. This calls the same openEvent<\/code> that was passed into the function.<\/p>\n

    To keep things simple in this tutorial, we\u2019ve bound a click to the <img \/><\/code> element. You should consider building something fancy that leverages a <button \/><\/code> for your production-ready blocks, for better accessibility support.<\/p>\n

    Right, that\u2019s our edit<\/code> function done. Not much code there, considering what it actually does, which is great! <\/p>\n

    Add our save function<\/h3>\n

    We\u2019ve got our Gutenberg editor-end of the block written now, which is the hard part. Now all we\u2019ve got to do is tell Gutenberg what we want the block to do with the content. With the same reactive data from attributes<\/code>, we can render out our front-end markup in real-time, too. That means when someone switches into HTML editing mode on the block, it\u2019ll be up to date. If you edit it in HTML editing mode, the visual mode will also be kept up to date. Super useful.<\/p>\n

    Let\u2019s dig in then. After our edit<\/code> function, add a comma, so it looks like },<\/code> and then add the following on a new line:<\/p>\n

    save({ attributes }) {\r\n\r\n  const cardImage = (src, alt) => {\r\n    if(!src) return null;\r\n\r\n    if(alt) {\r\n      return (\r\n        <img \r\n          className=\"card__image\" \r\n          src={ src }\r\n          alt={ alt }\r\n        \/> \r\n      );\r\n    }\r\n    \r\n    \/\/ No alt set, so let's hide it from screen readers\r\n    return (\r\n      <img \r\n        className=\"card__image\" \r\n        src={ src }\r\n        alt=\"\"\r\n        aria-hidden=\"true\"\r\n      \/> \r\n    );\r\n  };\r\n  \r\n  return (\r\n    <div className=\"card\">\r\n      { cardImage(attributes.imageUrl, attributes.imageAlt) }\r\n      <div className=\"card__content\">\r\n        <h3 className=\"card__title\">{ attributes.title }<\/h3>\r\n        <div className=\"card__body\">\r\n          { attributes.body }\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n  );\r\n}<\/code><\/pre>\n

    Looks pretty similar to the edit<\/code> function, right? Let\u2019s step through it.<\/p>\n

    We start of by using a destructuring assignment to pull out the attributes<\/code> from the passed paramaters, just like the previous edit<\/code> function. <\/p>\n

    Then we have another image helper function that firstly detects if there\u2019s an image and returns null<\/code> if there\u2019s not one. Remember: we return null<\/code> in JSX if we want it to render nothing. The next thing this helper does is render a slightly varied <img \/><\/code> tag if there\u2019s alt text or not. For the latter, it hides it from a screen-reader by adding aria-hidden=\"true\"<\/code> and setting a blank alt<\/code> attribute.<\/p>\n

    Lastly, our return<\/code> spits out a nice .card<\/code> block with clean, BEM-driven markup that will load on the front-end of our theme.<\/p>\n

    And that is that for our save<\/code> function. We\u2019re so close to having a completed block. Just one more step to go!<\/p>\n

    Add some style<\/h3>\n

    OK, we\u2019ve got this little bit to do and we\u2019re done. The observant amongst you may have noticed some references to className<\/code> dotted about. These are referencing our editor.scss<\/code> rules, so let\u2019s add them.<\/p>\n

    Open up editor.scss<\/code>, which lives in the same directory as block.js<\/code>. Add the following:<\/p>\n

    @import '..\/common';\r\n\r\n.gutenberg { \/\/ This may need to be .wp-block in WordPress 5+\r\n    \r\n  .container {\r\n    border: 1px solid $gray;\r\n    padding: 1rem;\r\n  }\r\n\r\n  .button-container {\r\n    text-align: center;\r\n    padding: 22% 0;\r\n    background: $off-white;\r\n    border: 1px solid $gray;\r\n    border-radius: 2px;\r\n    margin: 0 0 1.2rem 0;\r\n  }\r\n\r\n  .heading {\r\n    font-size: 1.5rem;\r\n    font-weight: 600;\r\n  }\r\n\r\n  .image {\r\n    height: 15.7rem;\r\n    width: 100%;\r\n    object-fit: cover;\r\n  }\r\n}<\/code><\/pre>\n

    This is some loose CSS to give our block some card-like style. Notice it\u2019s all nested within a .gutenberg<\/code> class? This is to battle the specificity of some core styles. Within the editor, there is a <div class=\"gutenberg\"<\/code> wrapped around the block area of the post editor screen, so we can make sure to only affect those elements with this nesting. You\u2019ll probably also notice that we\u2019re importing another Sass file, so let\u2019s fill that one. <\/p>\n

    Open common.scss<\/code> which lives in the src<\/code> directory, which is the parent of the current block<\/code> directory that we\u2019re in.<\/p>\n

    \/*\r\n * Common SCSS can contain your common variables, helpers and mixins\r\n * that are shared between all of your blocks. \r\n *\/\r\n\r\n\/\/ Colors\r\n$gray: #cccccc;\r\n$off-white: #f1f1f1;<\/code><\/pre>\n

    Anyway, guess what? We\u2019ve only gone and built out a custom card block!! Let\u2019s give it a test-drive.<\/p>\n

    First, check your block is all-good. This is what the complete block.js<\/code> file should look like: <\/p>\n

    const { RichText, MediaUpload, PlainText } = wp.editor;\r\nconst { registerBlockType } = wp.blocks;\r\nconst { Button } = wp.components;\r\n\r\n\/\/ Import our CSS files\r\nimport '.\/style.scss';\r\nimport '.\/editor.scss';\r\n\r\nregisterBlockType('card-block\/main', {   \r\n  title: 'Card',\r\n  icon: 'heart',\r\n  category: 'common',\r\n  attributes: {\r\n    title: {\r\n      source: 'text',\r\n      selector: '.card__title'\r\n    },\r\n    body: {\r\n      type: 'array',\r\n      source: 'children',\r\n      selector: '.card__body'\r\n    },\r\n    imageAlt: {\r\n      attribute: 'alt',\r\n      selector: '.card__image'\r\n    },\r\n    imageUrl: {\r\n      attribute: 'src',\r\n      selector: '.card__image'\r\n    }\r\n  },\r\n  edit({ attributes, className, setAttributes }) {\r\n\r\n    const getImageButton = (openEvent) => {\r\n      if(attributes.imageUrl) {\r\n        return (\r\n          <img \r\n            src={ attributes.imageUrl }\r\n            onClick={ openEvent }\r\n            className=\"image\"\r\n          \/>\r\n        );\r\n      }\r\n      else {\r\n        return (\r\n          <div className=\"button-container\">\r\n            <Button \r\n              onClick={ openEvent }\r\n              className=\"button button-large\"\r\n            >\r\n              Pick an image\r\n            <\/Button>\r\n          <\/div>\r\n        );\r\n      }\r\n    };\r\n\r\n    return (\r\n      <div className=\"container\">\r\n        <MediaUpload\r\n          onSelect={ media => { setAttributes({ imageAlt: media.alt, imageUrl: media.url }); } }\r\n          type=\"image\"\r\n          value={ attributes.imageID }\r\n          render={ ({ open }) => getImageButton(open) }\r\n        \/>\r\n        <PlainText\r\n          onChange={ content => setAttributes({ title: content }) }\r\n          value={ attributes.title }\r\n          placeholder=\"Your card title\"\r\n          className=\"heading\"\r\n        \/>\r\n        <RichText\r\n          onChange={ content => setAttributes({ body: content }) }\r\n          value={ attributes.body }\r\n          multiline=\"p\"\r\n          placeholder=\"Your card text\"\r\n          formattingControls={ ['bold', 'italic', 'underline'] }\r\n          isSelected={ attributes.isSelected }\r\n        \/>\r\n      <\/div>\r\n    );\r\n\r\n  },\r\n\r\n  save({ attributes }) {\r\n\r\n    const cardImage = (src, alt) => {\r\n      if(!src) return null;\r\n\r\n      if(alt) {\r\n        return (\r\n          <img \r\n            className=\"card__image\" \r\n            src={ src }\r\n            alt={ alt }\r\n          \/> \r\n        );\r\n      }\r\n      \r\n      \/\/ No alt set, so let's hide it from screen readers\r\n      return (\r\n        <img \r\n          className=\"card__image\" \r\n          src={ src }\r\n          alt=\"\"\r\n          aria-hidden=\"true\"\r\n        \/> \r\n      );\r\n    };\r\n    \r\n    return (\r\n      <div className=\"card\">\r\n        { cardImage(attributes.imageUrl, attributes.imageAlt) }\r\n        <div className=\"card__content\">\r\n          <h3 className=\"card__title\">{ attributes.title }<\/h3>\r\n          <div className=\"card__body\">\r\n            { attributes.body }\r\n          <\/div>\r\n        <\/div>\r\n      <\/div>\r\n    );\r\n  }\r\n});<\/code><\/pre>\n

    If you\u2019re happy, let\u2019s fire up webpack. While in your current plugin directory in terminal, run this: <\/p>\n

    npx webpack --watch<\/code><\/pre>\n

    Reader Moritz Karliczek wrote in to say they experienced an error here: TypeError: Cannot read property 'bindings' of null<\/code>. Our best guess right now is that this tutorial was written for Babel 6, but Babel 7 is out now. It’s up to you if you want to make sure your Babel stuff is locked down to 6 or if you want to sort out the 7 stuff on your own. If you’ve battled past this and have good info for us to update this series with, write in<\/a>!<\/p>\n

    Karen White hit us up having the same issue, and found that updating the `babelrc` file to use \"presets\": [\"@babel\/preset-env\"]<\/code> made it work.\n<\/p>\n

    This is slightly different to the previous part in the series because we\u2019ve added the --watch<\/code> argument. This basically keeps an eye on your js<\/code> files and re-runs webpack<\/code> if they change.<\/p>\n

    Fire up the editor!<\/h3>\n

    Let\u2019s load up the Gutenberg editor by loading up a post in the WordPress backend. In the Gutenberg editor, click the little plus icon and look in the \u201cblocks” tab and there it is: our awesome new card block!<\/p>\n

    \"\"<\/figure>\n

    Go ahead and give it a test drive and add some content in there. Feels good right? <\/p>\n

    Here\u2019s a quick video of what you should be seeing right now, with your fancy new card block:<\/p>\n