├── .gitignore ├── LICENSE.txt ├── README.md ├── screenshot.png ├── starter.json └── starter ├── .env.example ├── .eslintrc ├── gridsome.config.js ├── gridsome.server.js ├── package.json ├── src ├── .temp │ ├── config.js │ ├── constants.js │ ├── icons.js │ ├── now.js │ ├── plugins-client.js │ ├── plugins-server.js │ └── routes.js ├── components │ ├── Articles.vue │ ├── Card.vue │ ├── Navbar.vue │ └── README.md ├── favicon.png ├── index.html ├── layouts │ ├── Default.vue │ └── README.md ├── main.js ├── pages │ ├── Index.vue │ └── README.md ├── templates │ ├── Article.vue │ ├── Category.vue │ └── README.md └── utils │ ├── medias.js │ └── seo.js ├── static └── README.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .cache 3 | .DS_Store 4 | src/.temp 5 | node_modules 6 | dist 7 | .env 8 | .env.* 9 | !.env.example 10 | .vscode 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **:no_entry: Deprecated** 2 | 3 | This repository is no longer maintained and only works for Strapi v3. To find the newest Strapi v4 starters, check out the [starters-and-templates monorepo](https://github.com/strapi/starters-and-templates/). 4 | 5 | --- 6 | 7 | # Strapi Starter Gridsome Blog 8 | 9 | Gridsome starter for creating a great blog with Strapi. 10 | 11 | ![screenshot image](/screenshot.png) 12 | 13 | This starter allows you to try Strapi with Gridsome with the example of a simple blog. It is fully customizable and due to the fact that it is open source, fully open to contributions. So do not hesitate to add new features and report bugs! 14 | 15 | This starter uses the [Strapi blog template](https://github.com/strapi/strapi-template-blog) 16 | 17 | Check out all of our starters [here](https://strapi.io/starters) 18 | 19 | ## Features 20 | 21 | - 2 Content types: Article, Category 22 | - 2 Created articles 23 | - 3 Created categories 24 | - Responsive design using UIkit 25 | 26 | Pages: 27 | 28 | - "/" to display every articles 29 | - "/article/:id" to display one article 30 | - "/category/:id" display articles depending on the category 31 | 32 | ## Getting started 33 | 34 | Use our `create-strapi-starter` CLI to create your project. 35 | 36 | ```sh 37 | npx create-strapi-starter@3 my-project gridsome-blog 38 | ``` 39 | 40 | The CLI will create a monorepo, install dependencies, and run your project automatically. 41 | 42 | The Gridsome frontend server will run here => [http://localhost:8080](http://localhost:8080) 43 | 44 | The Strapi backend server will run here => [http://localhost:1337](http://localhost:1337) 45 | 46 | ## Deploying to production 47 | 48 | You will need to deploy the `frontend` and `backend` projects separately. Here are the docs to deploy each one: 49 | 50 | - [Deploy Strapi](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/deployment.html#hosting-provider-guides) 51 | - [Deploy Gridsome](https://gridsome.org/docs/deployment/) 52 | 53 | Don't forget to setup the environment variables on your production app: 54 | 55 | For the frontend the following environment variable is required: 56 | - `GRIDSOME_STRAPI_URL`: URL of your Strapi backend, without trailing slash 57 | 58 | 59 | Enjoy this starter! 60 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi/strapi-starter-gridsome-blog/9aa9b5fa0c871f8a663835e9495bafb6efd12427/screenshot.png -------------------------------------------------------------------------------- /starter.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "https://github.com/strapi/strapi-template-blog" 3 | } 4 | -------------------------------------------------------------------------------- /starter/.env.example: -------------------------------------------------------------------------------- 1 | GRIDSOME_STRAPI_URL=http://localhost:1337 -------------------------------------------------------------------------------- /starter/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "vue-eslint-parser", 3 | "parserOptions": { 4 | "ecmaVersion": 2020 5 | }, 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "extends": [ 11 | "plugin:gridsome/recommended", 12 | "eslint:recommended", 13 | "plugin:prettier/recommended" 14 | ], 15 | "rules": {} 16 | } 17 | -------------------------------------------------------------------------------- /starter/gridsome.config.js: -------------------------------------------------------------------------------- 1 | // This is where project configuration and plugin options are located. 2 | // Learn more: https://gridsome.org/docs/config 3 | 4 | // Changes here require a server restart. 5 | // To restart press CTRL + C in terminal and run `gridsome develop` 6 | 7 | module.exports = { 8 | siteName: "Strapi Gridsome Blog", 9 | siteDescription: "A blog site made using Gridsome and Strapi", 10 | plugins: [ 11 | { 12 | use: "@gridsome/source-graphql", 13 | options: { 14 | url: 15 | (process.env.GRIDSOME_STRAPI_URL || "http://localhost:1337") + 16 | "/graphql", 17 | fieldName: "strapi", 18 | typeName: "strapiTypes", 19 | }, 20 | }, 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /starter/gridsome.server.js: -------------------------------------------------------------------------------- 1 | // Server API makes it possible to hook into various parts of Gridsome 2 | // on server-side and add custom data to the GraphQL data layer. 3 | // Learn more: https://gridsome.org/docs/server-api/ 4 | 5 | // Changes here require a server restart. 6 | // To restart press CTRL + C in terminal and run `gridsome develop` 7 | 8 | module.exports = function (api) { 9 | api.createPages(async ({ graphql, createPage }) => { 10 | const { data } = await graphql(` 11 | { 12 | strapi { 13 | articles { 14 | slug 15 | } 16 | categories { 17 | slug 18 | } 19 | } 20 | } 21 | `); 22 | 23 | // Create blog articles pages. 24 | const articles = data.strapi.articles; 25 | const categories = data.strapi.categories; 26 | 27 | articles.forEach((article) => { 28 | createPage({ 29 | path: `/article/${article.slug}`, 30 | component: "./src/templates/Article.vue", 31 | context: { 32 | slug: article.slug, 33 | }, 34 | }); 35 | }); 36 | 37 | categories.forEach((category) => { 38 | createPage({ 39 | path: `/category/${category.slug}`, 40 | component: "./src/templates/Category.vue", 41 | context: { 42 | slug: category.slug, 43 | }, 44 | }); 45 | }); 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /starter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gridsome-starter-strapi", 3 | "private": true, 4 | "scripts": { 5 | "build": "gridsome build", 6 | "dev": "gridsome develop", 7 | "develop": "gridsome develop", 8 | "explore": "gridsome explore", 9 | "lint": "eslint --ext .js,.vue .", 10 | "lint:fix": "eslint --ext .js,.vue . --fix" 11 | }, 12 | "dependencies": { 13 | "@gridsome/source-graphql": "^0.1.0", 14 | "babel-runtime": "^6.26.0", 15 | "eslint-config-prettier": "^6.11.0", 16 | "eslint-plugin-prettier": "^3.1.4", 17 | "gridsome": "^0.7.0", 18 | "moment": "^2.27.0", 19 | "vue-markdown": "^2.2.4" 20 | }, 21 | "devDependencies": { 22 | "eslint": "^7.9.0", 23 | "eslint-plugin-gridsome": "^1.5.0", 24 | "prettier": "^2.1.2", 25 | "vue-eslint-parser": "^7.1.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /starter/src/.temp/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | "trailingSlash": true, 3 | "pathPrefix": "", 4 | "titleTemplate": "%s - Strapi Gridsome Blog", 5 | "siteUrl": "", 6 | "version": "0.7.23", 7 | "catchLinks": true 8 | } -------------------------------------------------------------------------------- /starter/src/.temp/constants.js: -------------------------------------------------------------------------------- 1 | export const NOT_FOUND_NAME = "404" 2 | export const NOT_FOUND_PATH = "/404" 3 | -------------------------------------------------------------------------------- /starter/src/.temp/icons.js: -------------------------------------------------------------------------------- 1 | export default { 2 | "touchiconMimeType": "image/png", 3 | "faviconMimeType": "image/png", 4 | "precomposed": false, 5 | "touchicons": [ 6 | { 7 | "width": 76, 8 | "src": "/assets/static/src/favicon.png?width=76&key=b4bc9f7" 9 | }, 10 | { 11 | "width": 152, 12 | "src": "/assets/static/src/favicon.png?width=152&key=b4bc9f7" 13 | }, 14 | { 15 | "width": 120, 16 | "src": "/assets/static/src/favicon.png?width=120&key=b4bc9f7" 17 | }, 18 | { 19 | "width": 167, 20 | "src": "/assets/static/src/favicon.png?width=167&key=b4bc9f7" 21 | }, 22 | { 23 | "width": 180, 24 | "src": "/assets/static/src/favicon.png?width=180&key=b4bc9f7" 25 | } 26 | ], 27 | "favicons": [ 28 | { 29 | "width": 16, 30 | "src": "/assets/static/src/favicon.png?width=16&key=4408023" 31 | }, 32 | { 33 | "width": 32, 34 | "src": "/assets/static/src/favicon.png?width=32&key=4408023" 35 | }, 36 | { 37 | "width": 96, 38 | "src": "/assets/static/src/favicon.png?width=96&key=4408023" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /starter/src/.temp/now.js: -------------------------------------------------------------------------------- 1 | export default 1623160307822 -------------------------------------------------------------------------------- /starter/src/.temp/plugins-client.js: -------------------------------------------------------------------------------- 1 | 2 | export default [ 3 | ] 4 | -------------------------------------------------------------------------------- /starter/src/.temp/plugins-server.js: -------------------------------------------------------------------------------- 1 | 2 | export default [ 3 | ] 4 | -------------------------------------------------------------------------------- /starter/src/.temp/routes.js: -------------------------------------------------------------------------------- 1 | const c1 = () => import(/* webpackChunkName: "page--src--templates--article-vue" */ "/Users/markkaylor/2021/starters/strapi-starter-gridsome-blog/starter/src/templates/Article.vue") 2 | const c2 = () => import(/* webpackChunkName: "page--src--templates--category-vue" */ "/Users/markkaylor/2021/starters/strapi-starter-gridsome-blog/starter/src/templates/Category.vue") 3 | const c3 = () => import(/* webpackChunkName: "page--node-modules--gridsome--app--pages--404-vue" */ "/Users/markkaylor/2021/starters/strapi-starter-gridsome-blog/starter/node_modules/gridsome/app/pages/404.vue") 4 | const c4 = () => import(/* webpackChunkName: "page--src--pages--index-vue" */ "/Users/markkaylor/2021/starters/strapi-starter-gridsome-blog/starter/src/pages/Index.vue") 5 | 6 | export default [ 7 | { 8 | path: "/article/what-s-inside-a-black-hole/", 9 | component: c1 10 | }, 11 | { 12 | path: "/article/we-love-pizza/", 13 | component: c1 14 | }, 15 | { 16 | path: "/article/the-internet-s-own-boy/", 17 | component: c1 18 | }, 19 | { 20 | path: "/article/this-shrimp-is-awesome/", 21 | component: c1 22 | }, 23 | { 24 | path: "/category/tech/", 25 | component: c2 26 | }, 27 | { 28 | path: "/category/story/", 29 | component: c2 30 | }, 31 | { 32 | path: "/category/nature/", 33 | component: c2 34 | }, 35 | { 36 | path: "/category/news/", 37 | component: c2 38 | }, 39 | { 40 | path: "/article/a-bug-is-becoming-a-meme-on-the-internet/", 41 | component: c1 42 | }, 43 | { 44 | path: "/category/food/", 45 | component: c2 46 | }, 47 | { 48 | path: "/article/beautiful-picture/", 49 | component: c1 50 | }, 51 | { 52 | name: "404", 53 | path: "/404/", 54 | component: c3 55 | }, 56 | { 57 | name: "home", 58 | path: "/", 59 | component: c4 60 | }, 61 | { 62 | name: "*", 63 | path: "*", 64 | component: c3 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /starter/src/components/Articles.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 47 | -------------------------------------------------------------------------------- /starter/src/components/Card.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 47 | -------------------------------------------------------------------------------- /starter/src/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 40 | query { 41 | strapi { 42 | categories { 43 | id 44 | name 45 | slug 46 | } 47 | global { 48 | siteName 49 | } 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /starter/src/components/README.md: -------------------------------------------------------------------------------- 1 | Add components that will be imported to Pages and Layouts to this folder. 2 | Learn more about components here: https://gridsome.org/docs/components/ 3 | 4 | You can delete this file. 5 | -------------------------------------------------------------------------------- /starter/src/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strapi/strapi-starter-gridsome-blog/9aa9b5fa0c871f8a663835e9495bafb6efd12427/starter/src/favicon.png -------------------------------------------------------------------------------- /starter/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ${head} 5 | 6 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ${app} 22 | ${scripts} 23 | 24 | 25 | -------------------------------------------------------------------------------- /starter/src/layouts/Default.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 67 | -------------------------------------------------------------------------------- /starter/src/layouts/README.md: -------------------------------------------------------------------------------- 1 | Layout components are used to wrap pages and templates. Layouts should contain components like headers, footers or sidebars that will be used across the site. 2 | 3 | Learn more about Layouts: https://gridsome.org/docs/layouts/ 4 | 5 | You can delete this file. 6 | -------------------------------------------------------------------------------- /starter/src/main.js: -------------------------------------------------------------------------------- 1 | // This is the main.js file. Import global CSS and scripts here. 2 | // The Client API can be used here. Learn more: gridsome.org/docs/client-api 3 | 4 | import DefaultLayout from "~/layouts/Default.vue"; 5 | 6 | export default function (Vue) { 7 | // Set default layout as a global component 8 | Vue.component("Layout", DefaultLayout); 9 | } 10 | -------------------------------------------------------------------------------- /starter/src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | query { 14 | strapi { 15 | global { 16 | siteName 17 | favicon { 18 | url 19 | } 20 | defaultSeo { 21 | metaTitle 22 | metaDescription 23 | shareImage { 24 | url 25 | } 26 | } 27 | } 28 | homepage { 29 | hero { 30 | title 31 | } 32 | seo { 33 | metaTitle 34 | metaDescription 35 | shareImage { 36 | url 37 | } 38 | } 39 | } 40 | articles { 41 | slug 42 | title 43 | category { 44 | name 45 | } 46 | image { 47 | url 48 | } 49 | author { 50 | name 51 | picture { 52 | url 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | 60 | 92 | -------------------------------------------------------------------------------- /starter/src/pages/README.md: -------------------------------------------------------------------------------- 1 | Pages are usually used for normal pages or for listing items from a GraphQL collection. 2 | Add .vue files here to create pages. For example **About.vue** will be **site.com/about**. 3 | Learn more about pages: https://gridsome.org/docs/pages/ 4 | 5 | You can delete this file. 6 | -------------------------------------------------------------------------------- /starter/src/templates/Article.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 50 | query($slug: String!) { 51 | strapi { 52 | articles(where: { slug: $slug }) { 53 | title 54 | description 55 | content 56 | published_at 57 | image { 58 | url 59 | } 60 | author { 61 | name 62 | picture { 63 | url 64 | } 65 | } 66 | } 67 | global { 68 | siteName 69 | favicon { 70 | url 71 | } 72 | defaultSeo { 73 | metaTitle 74 | metaDescription 75 | shareImage { 76 | url 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | 84 | 127 | -------------------------------------------------------------------------------- /starter/src/templates/Category.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | query($slug: String!) { 15 | strapi { 16 | categories(where: { slug: $slug }) { 17 | name 18 | articles { 19 | id 20 | title 21 | content 22 | slug 23 | image { 24 | url 25 | } 26 | category { 27 | name 28 | } 29 | author { 30 | name 31 | picture { 32 | url 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | 40 | 41 | 55 | -------------------------------------------------------------------------------- /starter/src/templates/README.md: -------------------------------------------------------------------------------- 1 | Templates for **GraphQL collections** should be added here. 2 | To create a template for a collection called `WordPressPost` 3 | create a file named `WordPressPost.vue` in this folder. 4 | 5 | Learn more: https://gridsome.org/docs/templates/ 6 | 7 | You can delete this file. 8 | -------------------------------------------------------------------------------- /starter/src/utils/medias.js: -------------------------------------------------------------------------------- 1 | export function getStrapiMedia(url) { 2 | // Check if URL is a local path 3 | if (url.startsWith("/")) { 4 | // Prepend Strapi address 5 | return `${ 6 | process.env.GRIDSOME_STRAPI_URL || "http://localhost:1337" 7 | }${url}`; 8 | } 9 | // Otherwise return full URL 10 | return url; 11 | } 12 | -------------------------------------------------------------------------------- /starter/src/utils/seo.js: -------------------------------------------------------------------------------- 1 | import { getStrapiMedia } from "./medias"; 2 | 3 | export function getMetaTags(seo) { 4 | const tags = []; 5 | 6 | if (seo.metaTitle) { 7 | tags.push( 8 | { 9 | property: "og:title", 10 | content: seo.metaTitle, 11 | }, 12 | { 13 | name: "twitter:title", 14 | content: seo.metaTitle, 15 | } 16 | ); 17 | } 18 | if (seo.metaDescription) { 19 | tags.push( 20 | { 21 | name: "description", 22 | content: seo.metaDescription, 23 | }, 24 | { 25 | property: "og:description", 26 | content: seo.metaDescription, 27 | }, 28 | { 29 | name: "twitter:description", 30 | content: seo.metaDescription, 31 | } 32 | ); 33 | } 34 | if (seo.shareImage) { 35 | const imageUrl = getStrapiMedia(seo.shareImage.url); 36 | tags.push( 37 | { 38 | name: "image", 39 | content: imageUrl, 40 | }, 41 | { 42 | property: "og:image", 43 | content: imageUrl, 44 | }, 45 | { 46 | name: "twitter:image", 47 | content: imageUrl, 48 | } 49 | ); 50 | } 51 | if (seo.article) { 52 | tags.push({ 53 | property: "og:type", 54 | content: "article", 55 | }); 56 | } 57 | tags.push({ name: "twitter:card", content: "summary_large_image" }); 58 | 59 | return tags; 60 | } 61 | -------------------------------------------------------------------------------- /starter/static/README.md: -------------------------------------------------------------------------------- 1 | Add static files here. Files in this directory will be copied directly to `dist` folder during build. For example, /static/robots.txt will be located at https://yoursite.com/robots.txt. 2 | 3 | This file should be deleted. --------------------------------------------------------------------------------