This is the third part of the tutorial series about creating static blog with Sapper, TailwindCSS and Github pages. In this part we are going to add categories and pagination to our blog.
First of all, let's create pagination component:
<!-- src/components/Pagination.svelte -->
<script>
export let prevLink = null, nextLink = null
</script>
{#if prevLink || nextLink}
<div class="flex justify-between border-t border-black py-4">
{#if prevLink}
<a
rel="prefetch"
href={prevLink}
class="text-gray-600 hover:text-gray-900 hover:underline">
Older
</a>
{/if}
{#if nextLink}
<a
rel="prefetch"
href={nextLink}
class="ml-auto text-gray-600 hover:text-gray-900 hover:underline">
Newer
</a>
{/if}
</div>
{/if}
And modify Item
component:
<a class="block mt-6" href={`/blog/${post.slug}`} rel="prefetch">
+ <h4 class="font-medium">
+ {#each post.categories as category, i}
+ <a
+ rel="prefetch"
+ href={`blog/category/${category}`}
+ class="text-gray-600 hover:text-gray-900 hover:underline">
+ {category}{i !== post.categories.length - 1 ? ', ' : ''}
+ </a>
+ {/each}
+ </h4>
<h3
Then, we need to modify src/routes/blog/_posts.js
:
// before
const { title, description, created, updated } = data
// after
const { title, description, created, updated, categories } = data
And also include categories
in object returned from this function.
As you might remember, this data comes from our .md
files with help of gray-mater package. So, now we can add categories field to all of our posts. For example:
---
categories: ['Svelte', 'Sapper', 'TailwindCSS']
---
Then, we will create src/utils.js
file with one constant and one function:
export const POSTS_PER_PAGE = 5;
export const getParams = params => {
const { rest } = params;
const category = rest[0] === "category" ? rest[1] : null;
const page = rest.length === 4 ? rest[3] : category ? 1 : rest[1];
return { category, page };
};
I am pretty sure you got what constant means, but for function, I will explain. We will support blog urls with formats:
/blog
/blog/category/{{category}}
/blog/page/{{page}}
/blog/category/{{category}}/page{{page}}
getParams
function is desired to take params from url, and extract category
and page
. The fallback value for category
is null, but for page
is 1.
Now, we can start modifying *.json.js
and route *.svelte
files. Let's start with simple one, src/routes/blog/latest.json.js
:
created: post.created,
- excerpt: post.excerpt
+ excerpt: post.excerpt,
+ categories: post.categories
};
Then, src/routes/blog/index.json.js
:
import posts from "./_posts.js";
import { POSTS_PER_PAGE } from "../../utils.js";
export function get(req, res) {
const paginated = posts.slice(0, POSTS_PER_PAGE);
const mapped = paginated.map(post => ({
title: post.title,
slug: post.slug,
created: post.created,
excerpt: post.excerpt,
categories: post.categories
}));
res.writeHead(200, {
"Content-Type": "application/json"
});
res.end(
JSON.stringify({
posts: mapped,
hasMore: posts.length > POSTS_PER_PAGE
})
);
}
Finally, modify src/routes/blog/index.svelte
. context="module"
part:
<script context="module">
export function preload({ params, query }) {
return this.fetch(`blog.json`)
.then(r => r.json())
- .then(posts => {
- return { posts };
+ .then(({ posts, hasMore }) => {
+ return { posts, hasMore };
});
}
</script>
Also, import Pagination
component, expose hasMore
prop, and add Pagination
to template:
<Pagination prevLink={hasMore ? 'blog/page/2' : null} />
Lastly, we need to add 2 more files to src/routes/blog
- [...rest].json.js and [...rest].svelte. You can see contents by following links.
Note: before starting to work on this part, I created more example posts in tutorial project using Lorem Markdownum.
Now, our blog is categorized and properly paginated, give a try by running:
yarn dev
and following http://localhost:3000
That's all for this part. Stay tuned, more interesting stuff is coming.