This is the frontend for blog.sling.biz built using Sling — a flexible content management system that allows for easy widget creation, dynamic page routing, and integration with your APIs.
This README outlines how the frontend of blog.sling.biz was created using Sling, starting from the creation of page templates, defining page routes, and creating custom widgets that help display blog data dynamically.
- Introduction
- Tech Stack
- Widgets Created
- Page Templates & Routes
- Sample Code & Explanation
- How to Run
- GitHub Repo
The blog.sling.biz site is a blog frontend that dynamically displays blog posts based on categories. It leverages Sling, a content management system (CMS), to make the frontend modular, reusable, and easy to maintain. The goal of this project was to allow users to select categories, fetch relevant blog posts from a backend API, and display them seamlessly.
- Frontend: Sling.biz (with hosted Studio to manage UI)
- Backend: Strapi (for managing blog data, categories, etc.)
- Widgets: Custom widgets built using Sling
- Styling: Material-UI (for UI components)
- Deployment: Vercel
This widget displays a list of categories on the left sidebar. It allows users to select a category, and based on their selection, it fetches the relevant blog posts.
Key Features:
- Displays a list of blog categories.
- Users can click on a category to filter the blog posts.
- Dynamically fetches data from Strapi’s API to show blog posts filtered by category.
import React, { useState, useEffect, useContext } from "react";
import { Box, Typography, TextField } from "@material-ui/core";
import AppContext from "../../utils/context/AppContext";
const CategoriesLeftBar = () => {
const [categories, setCategories] = useState([]);
const { selectedCategory, setSelectedCategory } = useContext(AppContext);
useEffect(() => {
const fetchCategories = async () => {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BLOG_API_URL}/api/categories`
);
const { data } = await response.json();
setCategories(data);
};
fetchCategories();
}, []);
return (
<Box>
<Typography variant="h6">Categories</Typography>
{categories.map((category, index) => (
<Typography
key={index}
onClick={() => setSelectedCategory(category.name)}
style={{ cursor: "pointer" }}
>
{category.name}
</Typography>
))}
</Box>
);
};
export default CategoriesLeftBar;This widget displays a list of blog posts based on the selected category. If no category is selected, it displays all posts.
Key Features:
- Displays blog posts in a grid layout.
- Each post contains an image, title, author, and read time.
- Fetches blog posts dynamically from the Strapi API based on the selected category.
import React, { useState, useEffect, useContext } from "react";
import { Box, Grid, Typography, CircularProgress } from "@material-ui/core";
import AppContext from "../../utils/context/AppContext";
const BlogsList = () => {
const [blogs, setBlogs] = useState([]);
const { selectedCategory } = useContext(AppContext);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchBlogs = async () => {
setLoading(true);
let fetchURL = `${process.env.NEXT_PUBLIC_BLOG_API_URL}/api/articles?sort[createdAt]=desc`;
if (selectedCategory) {
fetchURL = `${process.env.NEXT_PUBLIC_BLOG_API_URL}/api/articles?filters[categories][name][$eq]=${selectedCategory}&sort[createdAt]=desc`;
}
const response = await fetch(fetchURL);
const { data } = await response.json();
setBlogs(data);
setLoading(false);
};
fetchBlogs();
}, [selectedCategory]);
return (
<Box style={{ padding: "16px" }}>
{loading ? (
<Box display="flex" justifyContent="center" alignItems="center">
<CircularProgress color="primary" />
</Box>
) : blogs.length === 0 ? (
<Box textAlign="center">
<Typography variant="h5">No Posts Available</Typography>
</Box>
) : (
<Grid container spacing={3}>
{blogs.map((blog) => (
<Grid item xs={12} sm={6} md={4} key={blog.id}>
<Box>
<img
src={blog.image || "/images/default-image.png"}
alt={blog.title}
style={{ width: "100%" }}
/>
<Typography variant="h6">{blog.title}</Typography>
<Typography variant="body2">{blog.author}</Typography>
</Box>
</Grid>
))}
</Grid>
)}
</Box>
);
};
export default BlogsList;This widget shows the detailed view of a selected blog post. It fetches the blog post data using its unique slug from the Strapi API.
Key Features:
- Displays a single blog post's detailed content.
- The content is fetched dynamically based on the
slugpassed in the URL.
import React, { useState, useEffect } from "react";
import { Box, Typography } from "@material-ui/core";
const BlogDetail = ({ slug }) => {
const [blog, setBlog] = useState(null);
useEffect(() => {
const fetchBlogDetail = async () => {
const response = await fetch(
`${process.env.NEXT_PUBLIC_BLOG_API_URL}/api/articles?filters[slug][$eq]=${slug}`
);
const { data } = await response.json();
setBlog(data[0]);
};
fetchBlogDetail();
}, [slug]);
if (!blog) return <Typography>Loading...</Typography>;
return (
<Box>
<Typography variant="h3">{blog.title}</Typography>
<Typography variant="body1">{blog.content}</Typography>
</Box>
);
};
export default BlogDetail;The project uses page templates in Sling Studio to define the overall structure of the pages. Templates include the layout for the header, footer, and the content area where widgets are rendered.
Example template:
Ceate Page routes from Sling Studio.
-
Clone this repository:
git clone https://github.com/slingbiz/sling-blog-fe.git
-
Install dependencies:
cd blog.sling.biz npm install -
Create your account on Sling Studio and get your API key.
-
Update your .env file with your API key.
-
Start the development server:
npm run dev
-
Visit
http://localhost:4087in your browser.
You can view and contribute to the repository here.
Visit live blog at blog.sling.biz to see the implementation in action.