Предыдущие статьи

seo images for gatsby blog posts

Привет, в этой короткой заметке расскажу, как я прикрутил разные картинки к микроразметке своего блога. В пятой части серии (Блог на Gatsby и Obsidian ч.5) статей о создании с нуля своего блога на Gatsby я описывал создание темплейтов и одним из полей темплейтов было поле seoImage.

Предполагалось, что именно в этом поле я буду самостоятельно указывать картинку, которая затем будет прорастать в компонент SEO, который был написан в части #4 (Блог на Gatsby + Obsidian from scratch ч.4)

Но я понял, что я очень ленив, чтобы вручную указывать путь к картинке, к тому же такой способ обязал бы меня копировать изображения, в папку public, ведь во время сборки изображение копируется в папку static и путь к нему меняется.

Вообще, для чего нужна микроразметка ? Микроразметка на сайте нужна для того, чтобы помочь поисковым системам, таким как Google, лучше понимать структуру и содержание страницы. Плюс, во время шеринга ссылок на ваш блог в социальных сетях, при правильной разметке, люди будут видеть корректное превью с картинкой, заголовком и описанием, считаю, что этого достаточно, чтобы немного заморочиться на этот счет 😁

Решение

Я подумал, ведь в большинстве заметок, я и так указываю изображение в тексте и почему бы не использовать первое изображение в статье, как изображение для микроразметки.

Но парсить html в поисках тега img не очень хотелось. Я заметил, что можно вытащить из graphql не только итоговый html, но и ast деверо для html, с такой структурой довольно легко работать

Чтобы вытащить astHtml нужно просто добавить это поле в запрос в src/templates/BlogPost.js

export const query = graphql`
    query BlogPageQuery($slug: String!) {
        markdownRemark(fields: {slug: {eq: $slug}}) {
            html
            htmlAst <== Вот тут!
            excerpt
            frontmatter {
                date
                stage
            }
            fields {
                title
            }
        }
    }
`

Само содержимое htmlAst выглядит примерно так:

// Примерная структура ast дерева для поста
const htmlAst = {
	type: 'root',
	children: [
		{ 
			type: 'element',
			tagName: 'p',
			properties: {
				...
			},
			children: [
				{ 
					type: 'element',
					tagName: 'img',
					properties: {
						...,
						src: '/path/to/image' // <== Поле, которое нам нужно
					},
					children: [
						...
					]
				}
			]
		}
	]
}

Как видно из примера это довольно понятная рекурсивная структура, из которой мы легко сможем вытащить необходимое поле, функция которая этим занимается у меня получилась такая (оставил ее в том же файле src/templates/BlogPost.js):

// src/templates/BlogPost.js
function findFirstImage(astNode) {
    if (astNode?.type === 'element' && astNode?.tagName === 'img') {
      return astNode.properties.src;
    }
    
    let imgSrc = null;
    let i = 0;

    while (astNode && typeof astNode?.children?.[i] === 'object') {
        imgSrc = findFirstImage(astNode.children[i]);
        if (imgSrc) {
            break;
        }
        i++;
    }

    return imgSrc;
}

export const Head = (props) => {
    const { markdownRemark } = props.data;
    const { excerpt, fields, htmlAst } = markdownRemark;
    const { title } = fields;
    return (
        <SEO
	        image={findFirstImage(htmlAst)} // <== Добавил в компонент SEO дополнительное поле
	        title={title}
	        description={excerpt}
		/>
    )
}

Готово, теперь в теге <head /> в метатегах image и twitter:image будут подходящие картинки! Теперь после публикации обновленной версии блога можно увидеть результат, если переслать кому-нибудь ссылку на статью. Было бы очень здорово, если бы и эту статью кому-нибудь переслали и она оказалась полезной 😁