Skip to content

Commit e51151e

Browse files
authored
Gatsby multi-author support (#10)
1 parent 66b0848 commit e51151e

File tree

10 files changed

+623
-21243
lines changed

10 files changed

+623
-21243
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ logs
44
npm-debug.log*
55
yarn-debug.log*
66
yarn-error.log*
7+
package-json.lock
78

89
# Runtime data
910
pids

content/blog/finance-essentials-for-developers/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Essential Investing and Finance for developers
3-
date: "2020-05-10"
3+
date: "2020-04-20"
44
author: franleplant
55
description:
66
"Whether you are working on a fin tech startup or you want to start investing or
+286
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
---
2+
title: How to support multiple authors in Gatsby.js
3+
date: 2020-04-27
4+
author: franleplant
5+
description:
6+
"Gatsby Starter Blog doesn't support multiple authors by default. Let's fix that!
7+
And in the Journey we will uncover how to use the file system as source of dynamic data"
8+
tags:
9+
- GatsbyJs
10+
- Javascript
11+
- react
12+
- filesystem
13+
- yaml
14+
- blog
15+
- GraphQL
16+
---
17+
18+
## Introduction
19+
20+
[gatsby-starter-blog][7] comes with out of the box support for a single author blog.
21+
But what happens when you want to support multiple authors writing in your blog?
22+
23+
Gatsby actually makes it very easy to do so but for people getting started with Gatsby
24+
it might be a bit of challenge putting all the parts together.
25+
26+
This post hopes to show how to solve this problem holistically.
27+
28+
This blog is actually a real life implementation of the multi author blog. Check our [source][3]!
29+
30+
## Setting multiple author support
31+
32+
The highest level interface that authors writing posts will use will
33+
still be the [frontmatter][8] yaml on top of each post's markdown file.
34+
35+
By specifying an _author id_, we are going to be able
36+
to retrieve the rest of the author data we have stored in our codebase.
37+
38+
```markdown:title=content/blog/my-post/index.md
39+
---
40+
title: My Post!
41+
author: author1
42+
---
43+
```
44+
45+
For this to work we need to setup Gatsby to:
46+
47+
1. use the filesystem as a valid source of data. In this case we used `src/data` but it can be anything you like.
48+
2. use a yaml transformer because we will store our authors data inside a yaml but you could use JSON or any other format.
49+
3. map frontmatter's `author` attribute to `src/data/author.yaml`.
50+
51+
```javascript:title=gatsby-config.js
52+
module.exports = {
53+
plugins: [
54+
...otherPlugins,
55+
56+
// 1. use src/data as a data source
57+
{
58+
resolve: `gatsby-source-filesystem`,
59+
options: {
60+
path: `${__dirname}/src/data`,
61+
name: `data`,
62+
},
63+
},
64+
// 2. enable yaml support
65+
`gatsby-transformer-yaml`,
66+
],
67+
mapping: {
68+
// 3. map author to author.yaml
69+
"MarkdownRemark.frontmatter.author": `AuthorYaml`,
70+
},
71+
}
72+
```
73+
74+
You will probably need to install the necessary dependencies: `yarn add gatsby-source-filesystem gatsby-transformer-yaml`.
75+
76+
[Check Gatsby config docs](https://www.gatsbyjs.org/docs/gatsby-config/#plugins)
77+
78+
**Important:** frontmatter's `author` will need to match an `id` attribute in your `author.yaml`, since that is
79+
what the mapping uses as a sort of primary key.
80+
81+
Now lets show how the `author.yaml` looks.
82+
83+
```yaml:title=src/data/author.yaml
84+
- id: author1
85+
bio: I am Author 1
86+
profilepicture: ../../content/assets/author1.png
87+
- id: author2
88+
bio: I am Author 2
89+
profilepicture: ../../content/assets/author2.png
90+
```
91+
92+
At this point Gatsby will automatically provide the entire
93+
Author data via the GraphQL interface,
94+
in your post template.
95+
96+
Notice that it will return the data for the author you
97+
specified in the post's frontmatter, and of course, each
98+
post can have a different author.
99+
100+
Let's check how the post template can use the `author` data now:
101+
102+
```javascript:title=src/templates/blog-post.js
103+
export const pageQuery = graphql`
104+
query BlogPostBySlug($slug: String!) {
105+
markdownRemark(fields: { slug: { eq: $slug } }) {
106+
excerpt(pruneLength: 160)
107+
html
108+
frontmatter {
109+
title
110+
author {
111+
id
112+
bio
113+
profilepicture {
114+
childImageSharp {
115+
fluid {
116+
...GatsbyImageSharpFluid
117+
}
118+
}
119+
}
120+
}
121+
}
122+
}
123+
}
124+
`
125+
```
126+
127+
Inside your post component you will be able to access all author data:
128+
129+
```javascript:title=src/templates/blog-post.js
130+
export default function BlogPostTemplate(props) {
131+
const {
132+
id,
133+
bio,
134+
profilepicture,
135+
} = props.data.markdownRemark.frontmatter.author
136+
137+
//...
138+
}
139+
```
140+
141+
### Bonus: Author profile picture
142+
143+
If you look at the previous snippets you will notice we used the `profilepicture` to
144+
support author's profile pictures. Notice how in the GraphQL query that we showed before we used the
145+
`childImageSharp` query to enable image optimization, something that the Gatbsy Starter blog
146+
does by default in the mono-author mode.
147+
148+
By using this `childImageSharp` and any of the other queries of [gatsby-image][9] you can
149+
transform dynamically loaded images via the expected GraphQL interface with ease.
150+
151+
## How to use all authors
152+
153+
We've only covered the automagical way of mapping a frontmatter
154+
attribute to a filesystem database i.e. `author.yaml`.
155+
156+
But what happens when you want all the authors, lets say for a list of
157+
authors in your page or a shared [about](https://nosleepjavascript.com/about/) page?
158+
159+
Gatsby also has you covered with this by using the plugins we setup before.
160+
Gatsby will automatically provide a way of reading the entire `author.yaml` as follows:
161+
162+
```javascript:title=src/pages/about.js
163+
export const pageQuery = graphql`
164+
allAuthorYaml {
165+
nodes {
166+
id
167+
bio
168+
}
169+
}
170+
}
171+
`
172+
```
173+
174+
Notice how `author.yaml` is mapped to `allAuthorYaml`, that name transformation
175+
is significant and you should be carefully with it.
176+
177+
If unsure you can always go to the GraphQL explored that Gatsby provides by
178+
default and play around with the available queries.
179+
180+
At this point your page component will be provided with an array of authors:
181+
182+
```javascript:title=src/pages/about.js
183+
export default function About(props) {
184+
// this is an array of authors
185+
const authors = props.allAuthorYaml.nodes
186+
187+
//...
188+
}
189+
```
190+
191+
### Bonus: Run-Time multi author support through GraphQL
192+
193+
Notice that this setup also enables you do an ad-hoc manual implementation
194+
of the multi-author support.
195+
I will probably not recommend it for this use case but it is a possibility if you
196+
encounter a use case that might benefit from it. Notice that by using this you don't need
197+
to statically know the author id like we did in the `blog-post.js` before, you can
198+
do it entirely dynamically by simply passing react props.
199+
200+
```javascript:title=src/pages/Author.js
201+
export default function Author(props) {
202+
const authorId = props.author
203+
const authors = props.allAuthorYaml.nodes
204+
205+
// manually search for the selected author
206+
const myAuthor = authors.find((a) => a.id === authorId)
207+
208+
return <p>Author Bio: {myAuthor.bio}</p>
209+
}
210+
211+
export const query = graphql`
212+
allAuthorYaml {
213+
nodes {
214+
id
215+
bio
216+
}
217+
}
218+
}
219+
`
220+
```
221+
222+
And you can use it like:
223+
224+
```javascript
225+
<Author author="author1" />
226+
```
227+
228+
This can be extrapolated to a lot of other use cases.
229+
230+
### Bonus: Run-Time multi author support through importing files
231+
232+
And if everything else fails you can always simply import the yaml (or any other format) directly
233+
from your components and display the data however you like.
234+
235+
The main drawback is that you don't have some useful image transformations
236+
and some other GraphQL-only APIs but there might be very good use cases for this approach
237+
aside from supporting multiple authors:
238+
239+
```javascript:title=src/components/Author.js
240+
import authors from "../data/author.yaml"
241+
242+
export default function Author(props) {
243+
const authorId = props.author
244+
245+
// manually search for the selected author
246+
const myAuthor = authors.find((a) => a.id === authorId)
247+
248+
return <p>Author Bio: {myAuthor.bio}</p>
249+
}
250+
```
251+
252+
## Closing
253+
254+
I hope that I have eased the entry barrier for a multi author blog with Gatbsy,
255+
and taught some extra techniques in the way.
256+
257+
</br>
258+
259+
[1]: https://www.gatsbyjs.org/docs/gatsby-config/#mapping-node-types
260+
[2]: https://github.com/gatsbyjs/gatsby-starter-blog
261+
[3]: https://github.com/franleplant/nosleepjavascript-blog
262+
[4]: https://www.gatsbyjs.org/packages/gatsby-transformer-yaml/
263+
[5]: https://www.gatsbyjs.org/packages/gatsby-source-filesystem/
264+
[6]: https://github.com/franleplant/nosleepjavascript-blog
265+
[7]: https://www.gatsbyjs.org/starters/gatsbyjs/gatsby-starter-blog/
266+
[8]: https://www.gatsbyjs.org/packages/gatsby-transformer-remark/
267+
[9]: https://www.gatsbyjs.org/packages/gatsby-image/
268+
269+
</br>
270+
Want to become a Javascript expert? This is nice place to start:
271+
</br>
272+
</br>
273+
274+
<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-na.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&OneJS=1&Operation=GetAdHtml&MarketPlace=US&source=ss&ref=as_ss_li_til&ad_type=product_link&tracking_id=franleplant-20&language=en_US&marketplace=amazon&region=US&placement=B084BNMN7T&asins=B084BNMN7T&linkId=8b137e903d39f2ffe9d05b679af42508&show_border=true&link_opens_in_new_window=true"></iframe>
275+
276+
<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-na.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&OneJS=1&Operation=GetAdHtml&MarketPlace=US&source=ss&ref=as_ss_li_til&ad_type=product_link&tracking_id=franleplant-20&language=en_US&marketplace=amazon&region=US&placement=B08634PZ3N&asins=B08634PZ3N&linkId=41b1c21effffe8f07f6a3f9556d010b2&show_border=true&link_opens_in_new_window=true"></iframe>
277+
278+
<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-na.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&OneJS=1&Operation=GetAdHtml&MarketPlace=US&source=ss&ref=as_ss_li_til&ad_type=product_link&tracking_id=franleplant-20&language=en_US&marketplace=amazon&region=US&placement=1491904151&asins=1491904151&linkId=1cf3fa04463aad92be6c68fd69a0c964&show_border=true&link_opens_in_new_window=true"></iframe>
279+
280+
<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-na.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&OneJS=1&Operation=GetAdHtml&MarketPlace=US&source=ss&ref=as_ss_li_til&ad_type=product_link&tracking_id=franleplant-20&language=en_US&marketplace=amazon&region=US&placement=1491904224&asins=1491904224&linkId=6deb74eba04d6d6f64eda93686b39beb&show_border=true&link_opens_in_new_window=true"></iframe>
281+
282+
<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-na.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&OneJS=1&Operation=GetAdHtml&MarketPlace=US&source=ss&ref=as_ss_li_til&ad_type=product_link&tracking_id=franleplant-20&language=en_US&marketplace=amazon&region=US&placement=1491904240&asins=1491904240&linkId=3022a1bcff37fcf599ba8b9ddcd8517f&show_border=true&link_opens_in_new_window=true"></iframe>
283+
284+
<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-na.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&OneJS=1&Operation=GetAdHtml&MarketPlace=US&source=ss&ref=as_ss_li_til&ad_type=product_link&tracking_id=franleplant-20&language=en_US&marketplace=amazon&region=US&placement=1491904194&asins=1491904194&linkId=879d759323b3c457b665eeb431537dab&show_border=true&link_opens_in_new_window=true"></iframe>
285+
286+
<div id="amzn-assoc-ad-388ed262-d0bf-4cf6-8458-5b5d84d8a3cd"></div><script async src="https://z-na.associates-amazon.com/onetag/v2?MarketPlace=US&instanceId=388ed262-d0bf-4cf6-8458-5b5d84d8a3cd"></script>

content/drafts/gatsby-simply-multi-author/index.md

-38
This file was deleted.

gatsby-config.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,18 @@ module.exports = {
5252
toHeading: 6,
5353
},
5454
},
55-
55+
{
56+
resolve: `gatsby-remark-images`,
57+
options: {
58+
maxWidth: 590,
59+
},
60+
},
61+
{
62+
resolve: "gatsby-remark-code-titles",
63+
options: {
64+
className: "code-block-title",
65+
},
66+
},
5667
`gatsby-remark-prismjs`,
5768
`gatsby-remark-copy-linked-files`,
5869
`gatsby-remark-smartypants`,

0 commit comments

Comments
 (0)