Unlock seamless workflows and faster delivery with our latest releases - Join the deep dive

Help needed to customize a block content in Sanity to create an email link.

18 replies
Last updated: Sep 21, 2021
Hey everyone, I'm brand new here and brand new to web dev too. 🙂 I just deployed my first website (ericrodgers.io ) using the sanity/gatsby portfolio template. I'm trying to figure out how to customize some block content to create a link that opens email. Ideally I would like that new feature to have an email icon in the block content menu bar. So I guess this would behave like the built in "link" feature, but it would have its own icon, it would validate for email, and it would prepend the anchor tag's href with "mailto: " and maybe even append it with ?subject &/or &body default text... In the block content editor I want to be able to highlight some text that says "get in touch", click an email icon in the block content menu next to the decorator and link options, and then enter my email address, so that on the front end gatsby side of my site anyone who is visiting my page can click the "get in touch" text and it will open their email and populate the "to:" field with my address. I hope this makes sense! Thanks for your help!
Sep 15, 2021, 6:14 PM
Thanks so much Racheal!
Sep 15, 2021, 7:08 PM
Feel free to come back and share code as you work through it!
Sep 15, 2021, 7:09 PM
I read and re-read the links you provided. I'm getting tripped up on the first step here! I was able to add a custom annotation with an icon to the block content menu bar in the Sanity UI, but it doesn't work properly. When I try to use it, the fields I've included in bioPortableText.js don't show up, it just has an unusable input field with the default text "[object Object]" inside of it. I've attached a screen capture of the UI. Here's my github repo (https://github.com/ericerodgers/portfolio ) and here's the code from bioPortableText.js:`import {MdEmail} from 'react-icons/md';`

export default {

name: 'bioPortableText',

type: 'array',

title: 'Excerpt',

of: [

{

title: 'Block',

type: 'block',

styles: [{title: 'Normal', value: 'normal'}],

lists: [],

marks: {

decorators: [

{title: 'Strong', value: 'strong'},

{title: 'Emphasis', value: 'em'},

{title: 'Code', value: 'code'}

],

annotations: [

{

name: 'link',

type: 'object',

title: 'URL',

fields: [

{

title: 'URL',

name: 'href',

type: 'url'

},

{

title: 'Open in new window',

name: 'blank',

type: 'boolean'

}

]

},

{

name: 'email',

type: 'object',

title: 'Email',

icon: MdEmail,

fields: [

{

title: 'Email address',

name: 'href',

type: 'string',

// validation from tutorial: <https://stordahl.dev/bits/sanity-email-validation>

validation: (Rule) =>

Rule.regex(
`/[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/,`

{

name: 'email', // Error message is "Does not match email-pattern"

invert: false // Boolean to allow any value that does NOT match pattern

}

)

}

]

}

]

}

}

]

}
Sep 17, 2021, 4:52 PM
Ok after digging around in my data structure, I saw that the "_type" in the markDefs for my custom email annotation was coming through as "email", which was the "name" I gave it. Confusing to me because I thought the name was arbitrary, but it seems to affect the data type and functionality here... So in bioPortableText.js I've changed it to
name: "span"
, and now _type shows up as "span" and everything finally works in the Sanity UI as I'd intended, and the email I enter there is accessible on the front end via a graphql query, in the markDefs object. Don't really understand why, but it works. But now I'm having trouble figuring out how to make the serializer work. My bio is rendered as PortableText... not sure how to set up the serializer or how to include it as a prop in the PortableText component... I've read all the docs and watched all the tutorials and been all over google... please help! 😩
Sep 17, 2021, 6:21 PM
Ok now after a lot of trial and error I got it to work... the only remaining problem being that when I click on my email link it triggers a page load. I guess I can live with that for now.
Sep 17, 2021, 7:11 PM
Here's what worked for me... serializers.js now looks like this: import React from 'react';
import {Figure} from "./figure";

const serializers = {
  types: {
    figure: Figure
  },
  marks: {
    span: ({mark, children}) => {
      // Read <https://css-tricks.com/use-target_blank/>
      const { blank, href } = mark
      const mailto = `mailto:${href}?subject="Hi!"&body="I love your website!"`
      return (
        <a href={mailto} target="_blank" rel="noopener">{children}</a>
      )
    },
    link: ({mark, children}) => {
      const { blank, href } = mark
      return blank ? (
        <a href={href} target="_blank" rel="noopener noreferrer">
          {children}
        </a>
      ) : (
        <a href={href}>{children}</a>
      )
  }
}
};

export default serializers;
I imported BlockContent and serializers into the my about.js page and I replaced the PortableText element with a BlockContent element that included the serializers={serializers} prop. I hacked my way through thisand I'm shocked that it works... If someone can review my code here and let me know if this is the correct way to do it I would be very appreciative! Also I'd love to solve the page reload problem... once again my repository is available here:
https://github.com/ericerodgers/portfolio . Thanks!
Sep 17, 2021, 7:21 PM
Hey Eric! Great progress! I wasn't able to take a look at this thread until now, since I was out sick for a few days. Super impressed with your ability to work through this! I was curious about you having to change the name of your custom object to 'span'. You should be able to use whatever name you'd like for the object, so long it matches the name under
marks
in your serializers. For example, if you named the object 'email', the serializer would have to be named 'email'. Did you not experience that behavior?
Also, just a guess, but I think if you disable
target='_blank'
you can stop the page from refreshing, but I could be wrong!
Sep 18, 2021, 5:38 PM
Hey Racheal! Thanks for circling back on this. Hope you're feeling better. You were right about disabling target='_blank', that has stopped the page refresh when the email link is clicked! As for the custom object name in bioPortableText.js, I have gone back and tinkered with it again. As soon as I change the name from "span" back to "email" (or to anything else) and refresh the Sanity UI in my local build environment, the input field becomes disabled, and has default text that says: [object, Object]. name: 'span' is the only thing that gets it to work in my Sanity UI. This happens whether or not I match the new name with the name in the serializers.js file in my web (gatsby) folder. Looks to me like a bug, but I'm very new to web dev and 99.99% of the time things are going wrong for me it's my fault! Have you tried forking my repo and running it locally to see if it works like that for you?
Sep 20, 2021, 9:59 PM
I was able to clone your repo, but the annotation is behaving as it should be. One issue you might be running into is that it turns out that
email
is a reserved keyword. I didn't get a warning about it when it was inside of a portable text array, but I did if I pulled it out into its own object.
When I intentionally tried to break it, I couldn't get the behavior you do. The only thing that happened was the modal for the annotation disappeared.
Sep 20, 2021, 11:20 PM
I was able to clone your repo, but the annotation is behaving as it should be. One issue you might be running into is that it turns out that
email
is a reserved keyword. I didn't get a warning about it when it was inside of a portable text array, but I did if I pulled it out into its own object.
When I intentionally tried to break it, I coul
Sep 20, 2021, 11:20 PM
Can you share the JSON of the document that this is happening in? Click the kebab menu at the top right and select Inspect to get it.
Sep 20, 2021, 11:21 PM
I thought "email" might be a reserved keyword, so I tried using a couple other random words, but everything except "span" breaks the functionality for me. I'll paste the JSON below... The text i'm highlighting to turn into an email link is at bio[3].children[1].marks.text and the email link itself is at bio[3].markDefs[0].href.
Sep 21, 2021, 3:37 PM
bio:[] 6 items
0:{} 5 items
_key:074b1d0fcbef
_type:block
children:[] 1 item
0:{} 4 items
_key:5f8de82acdf80
_type:span
marks:[] 0 items
text:Hey! About me... I'm a freelance web developer, always in search of new projects. I also like to play guitar and write songs, I love to travel, and I have what some people might say is pretty pretentious taste in movies... but the web development part is probably the reason you're here.
markDefs:[] 0 items
style:normal
1:{} 5 items
_key:3a062be61f3f
_type:block
children:[] 5 items
0:{} 4 items
_key:5d2330099df6
_type:span
marks:[] 0 items
text:I first became interested in building websites several years ago after setting up some personal online small businesses with Etsy and Shopify. From there I found some part-time work doing e-commerce admin for brands like
1:{} 4 items
_key:b2708a963bd8
_type:span
marks:[] 1 item
0:db986ca35dfe
text:JED
2:{} 4 items
_key:b8c334b1fd1d
_type:span
marks:[] 0 items
text: and
3:{} 4 items
_key:6e7c1ffb4d07
_type:span
marks:[] 1 item
0:3f80f5ff5b54
text:Eric Weiner
4:{} 4 items
_key:22cdeb0378a8
_type:span
marks:[] 0 items
text:. This exposed me to the Sanity content manager and got me interested in learning a bit about coding. So I dove in and taught myself HTML, CSS, and JavaScript, and from there I moved on to React, Gatsby, Sanity, ES6, Sass, TypeScript... I learned how to use the terminal and git/github, code editors, how to build sites from scratch and ultimately how to deploy and host them on custom URLs with Netlify. These days I'm interested in continuing to learn new languages and frameworks such as Solidity (for developing on the Ethereum blockchain) and Next.js, to name a couple.
markDefs:[] 2 items
0:{} 4 items
_key:db986ca35dfe
_type:link
blank:true
href:<https://jedofficial.com/>
1:{} 4 items
_key:3f80f5ff5b54
_type:link
blank:true
href:<https://ericaweiner.com/>
style:normal
2:{} 5 items
_key:85819524274d
_type:block
children:[] 1 item
0:{} 4 items
_key:07f9284f61e8
_type:span
marks:[] 0 items
text:I also have experience with - and am fully proficient at - writing and editing copy, editing and resizing images with Photoshop, and collaborating with teams on new product launches.
markDefs:[] 0 items
style:normal
3:{} 5 items
_key:26a1c7da2a3c
_type:block
children:[] 3 items
0:{} 4 items
_key:7951d434123e
_type:span
marks:[] 0 items
text:This website is meant to serve as my portfolio, so poke around and check out some of the work I've done, which includes this site itself. If you're interested in learning more or discussing plans for your next site, or if you think I might make a good addition to your development team, please
1:{} 4 items
_key:28849ad817f1
_type:span
marks:[] 1 item
0:8f9af4f131c2
text:get in touch
2:{} 4 items
_key:2a55da22f51f
_type:span
marks:[] 0 items
text:! I'm currently based in the NYC area but am available for remote work anytime, any place.
markDefs:[] 1 item
0:{} 3 items
_key:8f9af4f131c2
_type:span
href:eric@ericrodgers.io
style:normal
4:{} 5 items
_key:45f454ee5735
_type:block
children:[] 1 item
0:{} 4 items
_key:fceef7bd35a1
_type:span
marks:[] 0 items
text:Thanks for stopping by!
markDefs:[] 0 items
style:normal
5:{} 5 items
_key:2851d159c907
_type:block
children:[] 1 item
0:{} 4 items
_key:aee8777edf37
_type:span
marks:[] 0 items
text:Eric
markDefs:[] 0 items
style:normal
image:{} 2 items
_type:figure
alt:blank
name:Me
slug:{} 2 items
_type:slug
current:me
Sep 21, 2021, 3:37 PM
oof sorry, i didn't realize it would lose the indentation formatting.... I thought putting it in a code block would retain that...
Sep 21, 2021, 3:38 PM
Here's a screen capture of the specific block of JSON under bio[3] showing the span text "get in touch" and the span link "eric@ericrodgers.io "
Sep 21, 2021, 3:42 PM
OK! What's likely happening is that the object was named 'span' when it was first created, so the
_type
of that bit of email text is set to
span
in your document's data. If you change the name of the object to 'contact', that piece of data can no longer be interacted with by the same schema that originally defined it because the Studio is looking for an entirely different
_type
.
You'll need to change the
_type
on that piece of data in order for it to work with a new name. You can either mutate it using the API (which is probably too complex of a task for just 1 mutation) or you can change the object's name in your schema &gt; save to hot reload the Studio &gt; remove the text that's tied to the old object's name &gt; reenter the text &gt; re-add the email annotation.
Sep 21, 2021, 6:15 PM
Hm... I'm not sure I understand how your suggestion is any different to what I've been trying: changing the name of the object to "contact" (or "whatever") in my schema and then saving (which hot reloads the Studio), then updating my serializer to match, then attempting to add the contact link to any text at all. That's what I've been doing, and it still won't work for me unless the object's name in the schema is "span." ☹️ And when I created the object from scratch the first time it was named "email". BUT after my most recent save I ran
npm run graphql-deploy
to make sure my schema changes were updated in my graphql playground, and in graphql I can see that everything - the normal text, my external url links, and my email contact link - are all
"_type": "span"
. Not sure if that offers any clues... ANYWAY... I'm just happy it works as is for now... if you are getting it to work off my cloned repo then I'm guessing I'm just misunderstanding something here on my end cuz I'm a newb! Thanks so much for your help Racheal!
Sep 21, 2021, 6:52 PM
I think the important difference is that you need to remove the text then re-add it and the annotation after you change the name of the schema, otherwise those changes will not be reflected in your actual content. But if it works and you're happy with it as is, that's all that matters!
Sep 21, 2021, 6:56 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?