This is a React SPA blog built on top of Contentful Headless CMS.
Running version: https://blog.rpenha.net/
- Frontend:
- ReactJS: https://react.dev/
- React Router: https://reactrouter.com/
- Vite: https://vitejs.dev/
- Bootstrap with SASS customization: https://getbootstrap.com/
- SASS: https://sass-lang.com/
- Terser (for JS minification and splitting): https://terser.org/
- PurgeCSS (for minimal CSS bundle size): https://purgecss.com/
- Package Manager: pnpm: https://pnpm.io/
- Hosting: AWS Cloudfront & AWS S3 Static Web App Hosting: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html#using-s3-as-origin
- Headless CMS: Contentful: https://www.contentful.com/
The following environment variables are required and should be defined in a .env.production
file:
VITE_CONTENTFUL_SPACE_ID
: Your Contentful Space IDVITE_CONTENTFUL_ACCESS_TOKEN
: Your Contentful Delivery API Access Token (Important: Keep this secret!).
More information about configuring Vite environment variables can be found here: https://vitejs.dev/guide/env-and-mode#env-variables-and-modes
Note: You should never commit the .env.production
file to version control.
-
Clone this repository.
-
Install dependencies:
pnpm install
-
Create a
.env.production
file in the project root directory and add the required environment variables (see above). -
Build the project for production:
pnpm run build
This will create an optimized production build in the
dist
folder. -
Deploy the
dist
folder to your AWS S3 bucket and configure Cloudfront for distribution.
-
Clone this repository.
-
Install dependencies:
pnpm install
-
Create a
.env.local
file in the project root directory and add the required environment variables (see above). -
Build the project for development:
pnpm run dev
This will start a local copy of the app on http://localhost:5173/ for development.
Note: You should never commit the .env.local
file to version control.
This blog utilizes Contentful as a headless CMS for managing blog content. Content types and models are defined within the Contentful platform.
Important: Ensure you configure the correct VITE_CONTENTFUL_SPACE_ID
and VITE_CONTENTFUL_ACCESS_TOKEN
environment variables to connect to your Contentful space.
{
"name": "Post",
"description": "",
"displayField": "title",
"fields": [
{
"id": "title",
"name": "Title",
"type": "Symbol",
"localized": false,
"required": true,
"validations": [],
"disabled": false,
"omitted": false
},
{
"id": "slug",
"name": "Slug",
"type": "Symbol",
"localized": false,
"required": true,
"validations": [],
"disabled": false,
"omitted": false
},
{
"id": "author",
"name": "Author",
"type": "Link",
"localized": false,
"required": true,
"validations": [
{
"linkContentType": [
"author"
]
}
],
"disabled": false,
"omitted": false,
"linkType": "Entry"
},
{
"id": "category",
"name": "Category",
"type": "Link",
"localized": false,
"required": true,
"validations": [
{
"linkContentType": [
"category"
]
}
],
"disabled": false,
"omitted": false,
"linkType": "Entry"
},
{
"id": "summary",
"name": "Summary",
"type": "Symbol",
"localized": false,
"required": true,
"validations": [],
"disabled": false,
"omitted": false
},
{
"id": "cover",
"name": "Cover",
"type": "Link",
"localized": false,
"required": false,
"validations": [
{
"linkMimetypeGroup": [
"image"
]
},
{
"assetImageDimensions": {
"width": {
"min": 800,
"max": null
},
"height": {
"min": 200,
"max": null
}
}
}
],
"disabled": false,
"omitted": false,
"linkType": "Asset"
},
{
"id": "body",
"name": "Body",
"type": "RichText",
"localized": false,
"required": true,
"validations": [
{
"enabledMarks": [
"bold",
"italic",
"underline",
"code",
"superscript",
"subscript"
],
"message": "Only bold, italic, underline, code, superscript, and subscript marks are allowed"
},
{
"enabledNodeTypes": [
"heading-2",
"heading-3",
"heading-4",
"heading-5",
"heading-6",
"ordered-list",
"unordered-list",
"hr",
"blockquote",
"embedded-entry-block",
"embedded-asset-block",
"table",
"hyperlink",
"entry-hyperlink",
"asset-hyperlink",
"embedded-entry-inline"
],
"message": "Only heading 2, heading 3, heading 4, heading 5, heading 6, ordered list, unordered list, horizontal rule, quote, block entry, asset, table, link to Url, link to entry, link to asset, and inline entry nodes are allowed"
},
{
"nodes": {}
}
],
"disabled": false,
"omitted": false
}
],
"sys": {
"space": {
"sys": {
"type": "Link",
"linkType": "Space",
"id": "xj8ro83rjak0"
}
},
"id": "post",
"type": "ContentType",
"createdAt": "2024-04-10T00:43:34.084Z",
"updatedAt": "2024-04-15T20:26:56.814Z",
"environment": {
"sys": {
"id": "master",
"type": "Link",
"linkType": "Environment"
}
},
"publishedVersion": 63,
"publishedAt": "2024-04-15T20:26:56.814Z",
"firstPublishedAt": "2024-04-10T00:43:34.461Z",
"createdBy": {
"sys": {
"type": "Link",
"linkType": "User",
"id": "3KHPR1faSdIgPMY8cWpX4y"
}
},
"updatedBy": {
"sys": {
"type": "Link",
"linkType": "User",
"id": "3KHPR1faSdIgPMY8cWpX4y"
}
},
"publishedCounter": 32,
"version": 64,
"publishedBy": {
"sys": {
"type": "Link",
"linkType": "User",
"id": "3KHPR1faSdIgPMY8cWpX4y"
}
},
"urn": "crn:contentful:::content:spaces/xj8ro83rjak0/environments/master/content_types/post"
}
}
+---------------+
| User |
+---------------+
|
v
+---------------+ +----------------+
| Browser |------->| Contentful |
+---------------+ +----------------+
|
v
+---------------+ +----------------+
| Blog Domain |------->| Route 53 |
+---------------+ +----------------+
|
v
+-----------------+ +-----------------+
| CloudFront |------>| AWS Certificate |
+-----------------+ | Manager |
| S3 Origin | +-----------------+
+-----------------+
|
v
+-------------------+
| S3 Content Bucket |
+-------------------+
- This project utilizes a customized SASS version of Bootstrap for styling consistency.
- Code splitting and minification are handled by Terser to improve application performance.
- PurgeCSS ensures the final CSS bundle only contains styles used by the application components.