Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Anchor Links From Sanity in Gatsby
CodingCatDev profile imageAlex Patterson
Alex Patterson forCodingCatDev

Posted on • Originally published atajonp.com on

     

Anchor Links From Sanity in Gatsby

Anchor Links 🔗 from Sanity.io in Gatsby

TL;DR version is make sure you implementonRouteUpdate andshouldUpdateScroll ingatsby-browser.js.

So what is an anchor link?

Anchor links are a way to navigate within the same page in HTML. The easiest way to think of them are like a table of contents, or bookmarks on a page. You will see anchors used often in markdown pages that have header tags in the form of#. Now in order for those normal header tags to have a link they must be wrapped on the front end with a link tag, similar to this:<a href="#anchor"><h2>Headline Link</h2></a>. If you inspect the code on this page, you will even see an example of just that, as the blog is written in markdown and converted to HTML.

Sanity.io

How does this work with Sanity.io

Sanity is a headless content based CMS. You write in arich text editor, which createsportable text. So unlike markdown you wont have to convert header# items but you will have to serialize the portable text into something that Gatsby can understand. I won't dive too deeply into how you create a site using sanity.io there are somegreat guides for that usinggatsby-source-sanity.

Extending the Sanity Gatsby blog

Sanity.io's Gatsbyblog example, is a great starting point for how to get up and running quickly. You can use that and then extend the functionality however you would like. In the example there is a file for posts which looks similar to below, what we really care about is the line<div>{_rawBody && <PortableText blocks={_rawBody} />}</div>.

import React from "react";import PortableText from "./portableText";import Card from "../Card";export default (props) => {  const { _rawBody, authors, categories } = props;  return(  <article className="flex flex-col w-full max-w-xl md:max-w-1xl md:max-w-2xl lg:max-w-3xl xl:max-w-6xl m-2 md:max-m-8 md:max-m-8 lg:max-m-8 xl:m-8">    <div className="w-full">    <Card {...props} />    </div>    <section className="markdown bg-white w-full rounded mt-4 p-4">    <div>{_rawBody && <PortableText blocks={_rawBody} />}</div>    <div>      <aside>        {categories && (          <div>            <h3>Categories</h3>            <ul>              {categories.map(category => (                <li key={category._id}>{category.title}</li>              ))}            </ul>          </div>        )}      </aside>    </div>    </section>  </article>  )}
Enter fullscreen modeExit fullscreen mode

This is a simple react component that uses@sanity/block-content-to-react. The great part here is that they have allowed for serializers and you can add a great deal of customization to any of the block based PortableText that you will be receiving from the graphql from Sanity.io.

import React from "react";import clientConfig from "../../../client-config";import BasePortableText from "@sanity/block-content-to-react";import serializers from "../graphql/serializers";const PortableText = ({ blocks }) => {  return(  <BasePortableText    blocks={blocks}    serializers={{...serializers}}    {...clientConfig.sanity} />  )};export default PortableText;
Enter fullscreen modeExit fullscreen mode

Sanity.io Serializers

The great part about serializers is that you can provide any custom React component that you would like to handle any of the different types that are coming from Sanity.io.

import React from "react";import Figure from "./Figure";import Code from "./Code";import Img from "./Img";import Block from "./Block";const serializers = {  types: {    authorReference: ({ node }) => <span>{node.author.name}</span>,    mainImage: Figure,    code: Code,    img: Img,    block: Block  },};export default serializers;
Enter fullscreen modeExit fullscreen mode

A fun and simple example isimg although I could add much of this inline, I plan to usecloudinary's image manipulations to apply affects as I would like to my images. So I added a simple component calledImg that takes in the node and outputs a simple img tag with corresponding alt text.

import React from "react";export default ({ node }) => {  const { asset } = node;  return <img src={asset.src} alt={asset.alt} />;};
Enter fullscreen modeExit fullscreen mode

Now the same can hold true for all theblock type items that appear with portableText. Because we are using Sanity.io's awesome@sanity/block-content-to-react we really wouldn't have to do much here, but since again I am a lazy developer I want to make all those headings magically have anchor tags associated, but our portableText looks something like below:

So in order to make that happen we added theblock: Block serializer above which Sanity.io has a greatexample how to setup. My Block looks very similar but it is setting Gatsby Link tags inside each of these headings (well h2 and h3 for now).

import React from "react";import { IoMdLink } from "react-icons/io/";import { Link } from "gatsby";const slugs = require(`github-slugger`)()export default (props) => {    slugs.reset();    const style = props.node.style || 'normal';    // If Heading Level add Anchor Link    if (typeof location !== `undefined` && /^h\d/.test(style)) {      const level = style.replace(/[^\d]/g, '')      const slug = slugs.slug(props.children[0], false)      switch (level) {        case 'h3':          return <h3 id={slug} className="flex">{props.children}<Link to={`${location.pathname}#${slug}`}><div className="py-1 pl-1 rotateIn"><IoMdLink /></div></Link></h3>        default:            return <h2 id={slug} className="flex">{props.children}<Link to={`${location.pathname}#${slug}`}><div className="py-1 pl-1 rotateIn"><IoMdLink /></div></Link></h2>      }    }    return style === 'blockquote'    ? <blockquote>{props.children}</blockquote>    : <p>{props.children}</p>};
Enter fullscreen modeExit fullscreen mode

Adding Gatsby Anchor Links

Now even though, Knut Melvær has a great guide calledInternal and external links that covers in great detail how to add links to your front end, I am a fairly lazy developer and I don't want to manually select and add all of my anchor links so that is why I used the above method. This same approach can be made using markdown files usinggatsby-remark-autolink-headers.

The one missing piece I found is scrolling to the correct location within the page in Gatsby. In order to do this Gatsby provides some awesomebrowser-apis. In order for the scrolling to occur when the page is first loaded we need to useonRouteUpdate this will allow us to use the location and check for an existance forhash which is our anchor link. I also implementedshouldUpdateScroll as selecting an internal link did not trigger a route update, so this was needed without refresh.

gatsby-browser.js

/** * Implement Gatsby's Browser APIs in this file. * * See: https://www.gatsbyjs.org/docs/browser-apis/ */// You can delete this file if you're not using itexports.onRouteUpdate = ({location}) => {  anchorScroll(location);  return true;};exports.shouldUpdateScroll = ({  routerProps: { location },  getSavedScrollPosition}) => {  anchorScroll(location);  return true;}function anchorScroll(location){  // Check for location so build does not fail  if (location && location.hash) {    setTimeout(() => {      // document.querySelector(`${location.hash}`).scrollIntoView({ behavior: 'smooth', block: 'start' });      const item = document.querySelector(`${location.hash}`).offsetTop;      const mainNavHeight = document.querySelector(`nav`).offsetHeight;      window.scrollTo({top: item - mainNavHeight, left: 0, behavior: 'smooth'});    }, 0);  }}
Enter fullscreen modeExit fullscreen mode

Final result

A nice smooth scrolling screen on refresh and internal link click.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Looking for a community of developers? Come hang out with CodingCatDev today! ➡️➡️➡️ https://discord.com/invite/fRJTRjR

More fromCodingCatDev

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp