├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── assets └── mixins.scss ├── components ├── AppIcon.vue ├── AppMasthead.vue └── AppNav.vue ├── jsconfig.json ├── layouts └── default.vue ├── middleware └── README.md ├── netlify.toml ├── nuxt.config.js ├── package-lock.json ├── package.json ├── pages ├── about.vue ├── blog │ └── _slug.vue └── index.vue ├── plugins ├── dateformat.js ├── posts.server.js └── tags.server.js ├── static ├── favicon.ico ├── icon.png ├── lake-photo.jpg └── mountains-masthead.jpg ├── store └── index.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | '@nuxtjs', 12 | 'plugin:nuxt/recommended' 13 | ], 14 | // add your custom rules here 15 | rules: { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | .editorconfig 83 | 84 | # Service worker 85 | sw.* 86 | 87 | # Mac OSX 88 | .DS_Store 89 | 90 | # Vim swap files 91 | *.swp 92 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sarah Drasner and Geoff Graham 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Headless Wordpress on the JAMstack 2 | 3 | > _A quick template to get you started using Headless WordPress_ 4 | 5 | ### Deploy this Template 6 | 7 | Click the "Deploy to Netlify" button, and you can configure it to fit your needs. 8 | 9 | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/netlify-labs/headless-wp-nuxt?utm_source=github&utm_medium=headlesswp-sd&utm_campaign=devex) 10 | 11 | ![Headless WordPress Template Preview](https://i.ibb.co/4wkXsxj/fullpreview-smashing.png "Template preview") 12 | 13 | An [article explaining how we set this all up is available on Smashing Magazine](https://www.smashingmagazine.com/2020/02/headless-wordpress-site-jamstack/). 14 | 15 | ## Build Setup 16 | 17 | ```bash 18 | # install dependencies 19 | $ npm install 20 | 21 | # serve with hot reload at localhost:3000 22 | $ npm run dev 23 | 24 | # build for production and launch server 25 | $ npm run build 26 | $ npm run start 27 | 28 | # generate static project 29 | $ npm run generate 30 | ``` 31 | 32 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 33 | -------------------------------------------------------------------------------- /assets/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin fluid-type($properties, $min-vw, $max-vw, $min-value, $max-value) { 2 | @each $property in $properties { 3 | #{$property}: $min-value; 4 | } 5 | 6 | @media (min-width: $min-vw) { 7 | @each $property in $properties { 8 | #{$property}: calc( 9 | #{$min-value} + 10 | #{strip-unit($max-value - $min-value)} * 11 | (100vw - #{$min-vw}) / 12 | #{strip-unit($max-vw - $min-vw)} 13 | ); 14 | } 15 | } 16 | 17 | @media (min-width: $max-vw) { 18 | @each $property in $properties { 19 | #{$property}: $max-value; 20 | } 21 | } 22 | } 23 | 24 | @function strip-unit($number) { 25 | @if type-of($number) == "number" and not unitless($number) { 26 | @return $number / ($number * 0 + 1); 27 | } 28 | 29 | @return $number; 30 | } 31 | -------------------------------------------------------------------------------- /components/AppIcon.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 34 | 35 | -------------------------------------------------------------------------------- /components/AppMasthead.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /components/AppNav.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 25 | 26 | 57 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "~/*": ["./*"], 6 | "@/*": ["./*"], 7 | "~~/*": ["./*"], 8 | "@@/*": ["./*"] 9 | } 10 | }, 11 | "exclude": ["node_modules", ".nuxt", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 94 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "dist" 3 | command = "npm run generate" 4 | [build.environment] 5 | NODE_VERSION = "11" -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | let dynamicRoutes = () => { 3 | const routes = axios 4 | .get("https://css-tricks.com/wp-json/wp/v2/posts?page=1&per_page=20") 5 | .then(res => { 6 | return res.data.map(post => `/blog/${post.slug}`) 7 | }) 8 | console.log(routes) 9 | return routes 10 | } 11 | 12 | export default { 13 | mode: "universal", 14 | /* 15 | ** Headers of the page 16 | */ 17 | head: { 18 | title: process.env.npm_package_name || "", 19 | meta: [ 20 | { charset: "utf-8" }, 21 | { name: "viewport", content: "width=device-width, initial-scale=1" }, 22 | { 23 | hid: "description", 24 | name: "description", 25 | content: process.env.npm_package_description || "" 26 | } 27 | ], 28 | link: [ 29 | { rel: "icon", type: "image/x-icon", href: "/favicon.ico" }, 30 | { 31 | rel: "stylesheet", 32 | href: 33 | "https://fonts.googleapis.com/css?family=Alata|Open+Sans&display=swap" 34 | } 35 | ] 36 | }, 37 | /* 38 | ** Customize the progress-bar color 39 | */ 40 | loading: { color: "#fff" }, 41 | /* 42 | ** Global CSS 43 | */ 44 | css: ["~/assets/mixins.scss"], 45 | /* 46 | ** Plugins to load before mounting the App 47 | */ 48 | plugins: [ 49 | "~/plugins/posts.server.js", 50 | "~/plugins/tags.server.js", 51 | "~/plugins/dateformat.js" 52 | ], 53 | generate: { 54 | routes: dynamicRoutes 55 | }, 56 | /* 57 | ** Nuxt.js dev-modules 58 | */ 59 | buildModules: [], 60 | /* 61 | ** Build configuration 62 | */ 63 | build: { 64 | /* 65 | ** You can extend webpack config here 66 | */ 67 | extend(config, ctx) {} 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wp-nuxt", 3 | "version": "1.0.0", 4 | "description": "Headless WordPress with Nuxt", 5 | "author": "geoffgraham and sdras", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate" 12 | }, 13 | "dependencies": { 14 | "axios": "^0.19.0", 15 | "nuxt": "^2.0.0" 16 | }, 17 | "devDependencies": { 18 | "node-sass": "^4.13.1", 19 | "sass-loader": "^8.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pages/about.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /pages/blog/_slug.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 29 | 30 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 74 | 75 | 211 | -------------------------------------------------------------------------------- /plugins/dateformat.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | 3 | Vue.filter("dateformat", value => { 4 | return new Date(value).toLocaleDateString("en-GB", { 5 | year: "numeric", 6 | month: "long", 7 | day: "numeric" 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /plugins/posts.server.js: -------------------------------------------------------------------------------- 1 | export default async ({ store }) => { 2 | await store.dispatch('getPosts') 3 | } 4 | -------------------------------------------------------------------------------- /plugins/tags.server.js: -------------------------------------------------------------------------------- 1 | export default async ({ store }) => { 2 | await store.dispatch("getTags") 3 | } 4 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netlify-labs/headless-wp-nuxt/b36ab048f847a7b0fc558ceef8141a4771c4e5df/static/favicon.ico -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netlify-labs/headless-wp-nuxt/b36ab048f847a7b0fc558ceef8141a4771c4e5df/static/icon.png -------------------------------------------------------------------------------- /static/lake-photo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netlify-labs/headless-wp-nuxt/b36ab048f847a7b0fc558ceef8141a4771c4e5df/static/lake-photo.jpg -------------------------------------------------------------------------------- /static/mountains-masthead.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netlify-labs/headless-wp-nuxt/b36ab048f847a7b0fc558ceef8141a4771c4e5df/static/mountains-masthead.jpg -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | const siteURL = "https://css-tricks.com" 2 | 3 | export const state = () => ({ 4 | posts: [], 5 | tags: [] 6 | }) 7 | 8 | export const mutations = { 9 | updatePosts: (state, posts) => { 10 | state.posts = posts 11 | }, 12 | updateTags: (state, tags) => { 13 | state.tags = tags 14 | } 15 | } 16 | 17 | export const actions = { 18 | async getPosts({ state, commit, dispatch }) { 19 | if (state.posts.length) return 20 | 21 | try { 22 | let posts = await fetch( 23 | `${siteURL}/wp-json/wp/v2/posts?page=1&per_page=20&_embed=1` 24 | ).then(res => res.json()) 25 | 26 | posts = posts 27 | .filter(el => el.status === "publish") 28 | .map(({ id, slug, title, excerpt, date, tags, content }) => ({ 29 | id, 30 | slug, 31 | title, 32 | excerpt, 33 | date, 34 | tags, 35 | content 36 | })) 37 | 38 | commit("updatePosts", posts) 39 | } catch (err) { 40 | console.log(err) 41 | } 42 | }, 43 | async getTags({ state, commit }) { 44 | if (state.tags.length) return 45 | 46 | let allTags = state.posts.reduce((acc, item) => { 47 | return acc.concat(item.tags) 48 | }, []) 49 | allTags = allTags.join() 50 | 51 | try { 52 | let tags = await fetch( 53 | `${siteURL}/wp-json/wp/v2/tags?page=1&per_page=40&include=${allTags}` 54 | ).then(res => res.json()) 55 | 56 | tags = tags.map(({ id, name }) => ({ 57 | id, 58 | name 59 | })) 60 | 61 | commit("updateTags", tags) 62 | } catch (err) { 63 | console.log(err) 64 | } 65 | } 66 | } 67 | --------------------------------------------------------------------------------