Это продолжение серии статей:
- Блог на Gatsby + Obsidian from scratch ч.1
- Блог на Gatsby + Obsidian from scratch ч.2 <-- вы тут
- Блог на Gatsby + Obsidian from scratch ч.3
- Блог на Gatsby + Obsidian from scratch ч.4
- Блог на Gatsby и Obsidian ч.5
- Блог на Gatsby. Rss лента ч.6
- Блог на Gatsby. Разные изображения для микроразметки ч.7
В этой статье речь пойдет про подключение темы к блогу, который мы создали в Блог на Gatsby + Obsidian from scratch ч.1
Мне понравилась вот эта бесплатная тема для блога, но проблема в том, что с ходу у меня ничего не завелось, даже после того, как я конвертировал посты в common markdown, об этом я написал тут: Как конвертировать Obsidian vault в common markdown ?
Была куча ошибок и ворнингов по поводу отсутствия тех или иных полей в моих заметках, в общем я довольно сильно приуныл, потому что последнее, что я хочу - это править все мои написанные заметки для того, чтобы они подходили под это решение.
Поэтому мной было принято решение - идти по своему "особому" творческому пути 💩
В добавок ко всему по результатам первой части этой серии статей, обнаружилось парочка неприятных моментов за исключеним полного отсутствия дизайна:
- Не отображаются изображения
- Нет ссылок для пагинации, то есть сам механизм работает, но в интерфейсе это никак не отображается
- Нигде не задействована метаинформация об авторе
- Сейчас никак не обрабатываются теги и для них нет никакой странички
- Нет никаких sitemap и прочих robots.txt seo штук - а они нужны, чтобы то что я пишу, хоть кто-то читал
Исправляем отображение картинок
Опытным путем, исследуя шаблон для блога было установлено, что у меня не хватает плагинов, которые отвечают за обработку и отображение картинок.
Важное отступление: Я в своих заметках использую стандартный способ ссылаться на картинки, вот такой:
А, не тот, что зачастую используется в Obsidian
![[link-to-image.jpeg]]Поэтому я не знаю, насколько хорошо будут работать картинки, если вы используете вышеупомянутый синтаксис
Глубже, чем написано тут - я не вникал, потому что установив эти пакеты и поправив gatsby-config.js по аналогии с указанным выше шаблоном - все магическим образом заработало:
- Устанавливаем необходимые пакеты
npm install gatsby-transformer-sharp gatsby-plugin-sharp gatsby-plugin-image- Устанавливаем плагин для корректного отображения
gif
npm install gatsby-remark-gifs --legacy-peer-deps- Правим
gatsby-config.js(привожу, только часть, которая поменялась)
Было
{
resolve: "gatsby-transformer-remark",
options: {
plugins: [
{
resolve: 'gatsby-remark-obsidian',
options: {
titleToURL: (title) => `/${require('slugify')(title, { lower: true })}/`,
markdownFolder: `${__dirname}/content/knowledgebase`,
highlightClassName: 'highlight',
},
},
]
}
}Стало
{
resolve: "gatsby-transformer-remark",
options: {
plugins: [
'gatsby-remark-gifs',
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 630,
},
},
{
resolve: 'gatsby-remark-obsidian',
options: {
titleToURL: (title) => `/${require('slugify')(title, { lower: true })}/`,
markdownFolder: `${__dirname}/content/knowledgebase`,
highlightClassName: 'highlight',
},
},
]
}
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
'gatsby-plugin-image',
Добавляем ссылки для постраничной навигации на главную
Для пагинации уже передаются все необходимые данные через pageContext - это я описывал в первой части этой серии статей, вот так это выглядит в файле gatsby-node.js:
context: {
limit: POSTS_PER_PAGE,
skip: i * POSTS_PER_PAGE,
numPages,
currentPage: i + 1,
},Так что напишем самую простую реализацию, пока опустив стили:
Pagination component
src/components/Pagination/Pagination.js
import React from 'react'
import { Link } from 'gatsby'
export const Pagination = ({ pageContext }) => {
const { currentPage, numPages } = pageContext
const isFirst = currentPage === 1
const isLast = currentPage === numPages
const prevPage = currentPage - 1 === 1 ? '/' : `/page/${currentPage - 1}`
const nextPage = `/page/${currentPage + 1}`
return (
<div className="pagination">
{!isFirst && (
<Link to={prevPage} rel="prev">
←
</Link>
)}
{Array.from({ length: numPages }, (_, i) => (
<Link
key={`pagination-number${i + 1}`}
to={i === 0 ? '/' : `/page/${i + 1}`}
>
{i + 1 === currentPage ? (
<b>{i + 1}</b>
) : i + 1}
</Link>
))}
{!isLast && (
<Link to={nextPage} rel="next">
→
</Link>
)}
</div>
)
}src/components/Pagination/index.js
export { Pagination } from './Pagination'src/templates/BlogList.js
import { Pagination } from '../components/Pagination'
export default function BlogList(props) {
...
<Pagination pageContext={props.pageContext} />Проверяем!

Добавляем метаинформацию об авторе блога
Для отображения статической информации, которая не завязана на наличие каких-либо переменных можно и нужно использовать хук useStaticQuery. Его можно использовать прямо по месту, в теле любого компонента.
Тогда как запросы с переменными можно писать только компонентах уровня страниц, что мы и делали для страниц со списком постов и страницы поста
UserInfo component
src/componenst/UserInfo.js
import React from 'react'
import { useStaticQuery, graphql } from 'gatsby'
export function UserInfo() {
const data = useStaticQuery(graphql`
query {
site {
siteMetadata {
description
title
social {
siteUrl
twitter
twitterUsername
}
}
}
}
`);
const {
title,
description,
author,
} = data.site.siteMetadata;
const {
twitter,
twitterUsername,
} = data.site.siteMetadata.social;
return (
<div>
<h1>{title}</h1>
<p>{description}</p>
<p>{author}</p>
<a target={'_blank'} href={twitter}>{twitterUsername}</a>
</div>
)
}src/templates/BlogList.js
import { UserInfo } from '../components/UserInfo'
export default function BlogList(props) {
...
return (
<UserInfo />
...
Теперь на главной блога будет отображаться название и описание блога, а также ссылка на твиттер, позже можно без труда добавить туда еще информации, просто поправив gatsby-config.js
Самое время добавить немного стилей, чтобы блог не выглядел так ущербно. Подключим tailwind, с его помощью можно быстро накинуть классы на рзметку и получить вменяемый внешний вид сайта за достаточно короткое время 😁
Добавляем tailwind
Устанавливаем необходимые пакеты и создаем конфиг для tailwind
npm install -D tailwindcss postcss autoprefixer gatsby-plugin-postcss
npx tailwindcss init -pПосле выполнения последней команды у нас в корне проекта появилось два файлика c конфигурациями для tailwind и postcss:
tailwind.config.jspostcss.config.js
Указываем где хранятся наши компоненты в tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/pages/**/*.{js,jsx,ts,tsx}",
"./src/components/**/*.{js,jsx,ts,tsx}",
"./src/templates/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}Добавляем в gatsby-config.js в секцию plugins еще один плагин
plugins: [
'gatsby-plugin-postcss',
...
]Создаем файлик src/styles/global.css и подключаем в него tailwind
@tailwind base;
/*
* Описываем базовые стили для всего
* только так мы можем стилизовать статьи
*/
@layer base {
html {
@apply tracking-tighter text-slate-800;
}
h1 {
@apply mb-6 text-4xl text-black;
}
h2 {
@apply mt-4 mb-4 text-2xl font-medium text-black;
}
h3 {
@apply mt-2 mb-2 text-xl font-medium text-black;
}
p {
@apply text-lg;
}
.gatsby-resp-image-wrapper {
@apply mb-2 mt-2;
}
small {
@apply text-sm;
}
time {
@apply text-sm;
}
a {
@apply text-indigo-600 hover:underline hover:text-indigo-900 transition-all ease-in-out duration-200;
}
code {
@apply bg-sky-100
}
pre {
@apply bg-sky-100 p-8 rounded-lg my-4;
}
}
@tailwind components;
@tailwind utilities;Подключаем стили в файлике gatsby-browser.js. в корне проекта (если нет, то создаем)
import './src/styles/global.css'Добавляем еще немного стилей
Я решил немного упороться и добавить немного стилей в компоненты UserInfo и Pagination и накинуть соответсвующие классы для контейнеров, которые оборачивают весь контент.
Приведу в пример только часть кода, остальное вы сможете посмотреть в репозитории. Как добавлять классы, можно почитать в документации tailwind - там очень крутой поиск и сама дока очень подробная, что позволяет максимально быстро накидывать классы для того, что вам нужно.
/* src/templates/BlogPost.js */
<div
className='container mx-auto px-4 mt-16' // Контейнер, центрируется, с внутренними и верхним отступом
dangerouslySetInnerHTML={{ __html: html }}
/>
/**
* src/templates/Bloglist.js
*/
export const query = graphql`
query BlogListQuery($skip: Int!, $limit: Int!) {
allMarkdownRemark(
sort: { frontmatter: { date: DESC }}
limit: $limit
skip: $skip
) {
edges {
node {
...
excerpt <---- Добавил поле excerpt для превью текста статьи
}
}
`
/**
* Настроил вывод статей ввиде сетки
* количество колонок меняется
* в зависимости от размера экрана
*/
export default function BlogList(props) {
...
<UserInfo />
<div className='grid xs:grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8'>
{edges.map(({ node }) => {
const { slug, title } = node.fields;
const { date } = node.frontmatter;
return (
<div className="mb-4" key={slug}>
<h2 className="text-bold text-2xl">
<Link to={`/${slug}/`}>{title}</Link>
</h2>
<p className="text-gray-500">{node.excerpt}</p>
<span className="text-gray-300" >{date}</span>
</div>
)
})}
</div>
После того, как я немного поколдовал со стилями, получилось вполне приемлемо:

Подключаем wikilink плагин
Это нужно, чтобы корректно обрабатывать ссылки на другие статьи в формате
[[note name]]который используется в Obsidian, я зачастую использую именно его, потому что так очень быстро линковать статьи
npm i @idmyn/gatsby-remark-wiki-link
// gatsby-config.js
{
resolve: "gatsby-transformer-remark",
options: {
plugins: [
// ...остальные плагины
{
resolve: "@idmyn/gatsby-remark-wiki-link",
options: {
pageResolver: (name) => {
console.log('name', name);
return [name.replace(/ /g, '-').toLowerCase()]
},
hrefTemplate: (permalink) => {
return `/${slufify(permalink, { lower: true })}/`
}
}
},Подключаем prismjs
Последний штрих - подключим prismjs - чтобы красиво отображать фрагменты кода в статьях
npm install gatsby-remark-prismjs prismjsДобавляем еще один плагин в gatsby-transformer-remark. Подробнее про возможные опции и их назначение можно почитать тут
// gatsby-config.js
...
resolve: "gatsby-transformer-remark",
options: {
plugins: [
{
resolve: `gatsby-remark-prismjs`,
options: {
classPrefix: "language-",
aliases: { sh: "bash" }
}
},
...
Импортируем стили в gatsby-browser.js
// gatsby-browser.js
import 'prismjs/themes/prism-solarizedlight.css';Теперь сниппеты с кодом выглядят гораздо приятнее:
Тем для подсветки кода предоставляется несколько, так что можно поэксперементировать и вместо solarizedlight поставить другую:
darktwiilightokaidiatomorrowfunky
В следующей части разберемся со страницей тегов, выложим блог и прикрутим аналитику!











