├── .eslintrc.json ├── .github └── workflows │ └── nextjs.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── app ├── blogs │ └── [slug] │ │ └── page.tsx ├── favicon.ico ├── globals.css ├── layout.module.css ├── layout.tsx ├── not-found.module.css ├── not-found.tsx ├── p │ └── [current] │ │ └── page.tsx ├── page.tsx ├── search │ ├── p │ │ └── [current] │ │ │ └── page.tsx │ └── page.tsx └── tags │ └── [tagId] │ ├── layout.module.css │ ├── layout.tsx │ ├── p │ └── [current] │ │ └── page.tsx │ └── page.tsx ├── components ├── Article │ ├── index.module.css │ └── index.tsx ├── ArticleList │ └── index.tsx ├── ArticleListItem │ ├── index.module.css │ └── index.tsx ├── Date │ ├── index.module.css │ └── index.tsx ├── Footer │ ├── index.module.css │ └── index.tsx ├── Header │ ├── index.module.css │ └── index.tsx ├── Nav │ ├── index.module.css │ └── index.tsx ├── Pagination │ ├── index.module.css │ └── index.tsx ├── SearchField │ ├── index.module.css │ └── index.tsx ├── TagList │ ├── index.module.css │ └── index.tsx └── TagListItem │ ├── index.module.css │ └── index.tsx ├── constants └── index.ts ├── libs ├── microcms.ts └── utils.ts ├── microcms-template.json ├── next.config.js ├── package-lock.json ├── package.json ├── public ├── blog.png ├── clock.svg ├── cover.png ├── create-service.png ├── img-vercel-settings.png ├── logo.svg ├── no-image.png ├── ogp.png ├── page-preview-settings.png ├── publish.png ├── search.svg ├── signin-en.png ├── signin.png ├── tag.png └── writer.png └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "next/core-web-vitals", "prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/nextjs.yml: -------------------------------------------------------------------------------- 1 | name: Do Nothing 2 | 3 | on: 4 | repository_dispatch: 5 | types: [microcms] 6 | 7 | jobs: 8 | do-nothing: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Do Nothing 13 | run: echo "Doing nothing" 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict = true -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "simonsiefke.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true, 4 | "editor.defaultFormatter": "esbenp.prettier-vscode", 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": true 7 | }, 8 | "editor.formatOnSave": true 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 microCMS 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 | # Simple Blog with microCMS 2 | 3 | ![](public/cover.png) 4 | 5 | This is an official microCMS simple blog template. 6 | 7 | This blog is built using microCMS, a headless CMS made in Japan, and Next.js. 8 | 9 | This template supports authoring and editing the following blog content and information: 10 | 11 | - Blog details 12 | - Writer 13 | - Tags 14 | 15 | ## Demo 16 | 17 | https://simple-blog-with-microcms.vercel.app/ 18 | 19 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-description=This%20is%20a%20microCMS%20official%20simple%20blog%20template.&demo-image=https://github.com/microcmsio/simple-blog-with-microcms/blob/main/public/cover.png?raw=true&demo-title=Simple%20Blog%20with%20microCMS&demo-url=https://simple-blog-with-microcms.vercel.app/&from=templates&project-name=Simple%20Blog%20with%20microCMS&repository-name=simple-blog-with-microcms&repository-url=https://github.com/microcmsio/simple-blog-with-microcms&env=MICROCMS_API_KEY,MICROCMS_SERVICE_DOMAIN,BASE_URL) 20 | 21 | ## Configuration 22 | 23 | ### Create an account and a service on microCMS 24 | 25 | 1. Create an account on [microCMS](https://app.microcms.io/signup). 26 | 2. Create a new empty service from the [dashboard](https://app.microcms.io/create-service). When creating the service, please select "Create your own". 27 | 3. Enter any value you like for **Service Name** and **Service ID**. 28 | 4. You now have a new service created that you can access! 29 | 30 | ### Create APIs 31 | 32 | You will need to create three APIs, all in the **list** format. 33 | 34 | #### **Create `tag` API.** 35 | 36 | The `tag` API is for creating tags associated with blog content. Blog content can have multiple tags set within the tag API. 37 | 38 | 1. Go to `/create-api` (https://your-service-id.microcms.io/create-api) and select "Create your own". 39 | 2. Enter basic API information: 40 | - API Name: **tag** 41 | - Endpoint: **tags** 42 | 3. Select API Type and choose **List** Format. 43 | 4. Define API Schema. The `tag` API has one field: 44 | - `name` - **Text Field**. **Field ID** and **Display Name** should be set to `name`. 45 | 5. Click "Create" and continue. 46 | 47 | ![](public/tag.png) 48 | 49 | #### **Create `writer` API** 50 | 51 | The `writer` API is for creating author information and associating it with each blog article. A blog article can only have one writer set in the writer API. 52 | 53 | 1. Go to `/create-api` (https://your-service-id.microcms.io/create-api) and select "Create your own". 54 | 2. Enter basic API information: 55 | - API Name: **writer** 56 | - Endpoint: **writers** 57 | 3. Select API Type and choose **List** Format. 58 | 4. Define API Schema. The `writer` API has three fields: 59 | - `name` - **Text Field**. **Field ID** and **Display Name** should be set to `name`. 60 | - `profile` - **Text Area**. **Field ID** and **Display Name** should be set to `profile`. 61 | - `image` - **Image Field**. **Field ID** and **Display Name** should be set to `image`. 62 | 5. Click "Create" and continue. 63 | 64 | ![](public/writer.png) 65 | 66 | #### **Create `blog` API** 67 | 68 | The `blog` API is for creating blog content. 69 | 70 | 1. Go to `/create-api` (https://your-service-id.microcms.io/create-api) and select "Create your own". 71 | 2. Enter basic API information: 72 | - API Name: **blog** 73 | - Endpoint: **blog** 74 | 3. Select API Type and choose **List** Format. 75 | 4. Define API Schema. The `blog` API has six fields: 76 | - `title` - **Text Field**. **Field ID** and **Display Name** should be set to `title`. 77 | - `description` - **Text Area**. **Field ID** and **Display Name** should be set to `description`. 78 | - `content` - **Rich Text Editor**. **Field ID** and **Display Name** should be set to `content`. 79 | - `thumbnail` - **Image Field**. **Field ID** and **Display Name** should be set to `thumbnail`. 80 | - `tags` - **Multiple Content References - tag**. **Field ID** and **Display Name** should be set to `tags`. 81 | - `writer` - **Content Reference - writer**. **Field ID** and **Display Name** should be set to `writer`. 82 | 5. Click "Create" and continue. 83 | 84 | ![](public/blog.png) 85 | 86 | ### Enter Content 87 | 88 | 1. Enter content for the `tag` API, `writer` API, and `blog` API. Since the `tag` API and `writer` API are referenced from the `blog` API, please create the `blog` API content last. 89 | 90 | 2. Go to the `/apis/[tags|writers|blog]/create` endpoint, click "**+ Add**", enter content for each of the following and click publish: 91 | 92 | **tag** 93 | 94 | - You just need 1 tag content. 95 | - Use dummy data for the text. 96 | 97 | **writer** 98 | 99 | - You just need 1 writer content. 100 | - Use dummy data for the text and image. 101 | 102 | **blog** 103 | 104 | - You just need 1 blog content. 105 | - Use dummy data for the text and image. 106 | 107 | ![](public/publish.png) 108 | 109 | ### Running locally 110 | 111 | #### **Retrieve API Key** 112 | 113 | 1. From the top page of the dashboard, Go to **"/api-keys"**. 114 | 2. Copy and retrieve the value of the **"default"** API Key. 115 | 116 | This API key will be used to make requests to microCMS from Next.js. 117 | 118 | #### **Set environment variables** 119 | 120 | Create a `.env.local` file in the root of the project and enter the following microCMS values: 121 | 122 | ``` 123 | MICROCMS_API_KEY=xxxxxxxxxx 124 | MICROCMS_SERVICE_DOMAIN=xxxxxxxxxx 125 | BASE_URL=xxxxxxxxxx 126 | ``` 127 | 128 | - `MICROCMS_API_KEY`: Retrieve the API key from the microCMS dashboard under "Service Settings > API Keys". 129 | - `MICROCMS_SERVICE_DOMAIN`: Retrieve the service domain from the microCMS dashboard URL (https://xxxxxxxx.microcms.io). 130 | - `BASE_URL`: The deployment URL and protocol. For example: 131 | - Development: http://localhost:3000 132 | - Production: https://xxxxxxxx.vercel.app 133 | 134 | #### **Run the local development server** 135 | 136 | This application requires Node.js version 18 or higher. 137 | 138 | - Install package dependencies 139 | 140 | ```bash 141 | npm install 142 | ``` 143 | 144 | - Launch the local development web server 145 | 146 | ```bash 147 | npm run dev 148 | ``` 149 | 150 | - Open [http://localhost:3000](http://localhost:3000) in your browser and access your local application! 151 | 152 | ### Deploy your project to Vercel 153 | 154 | 1. [Import your repository](https://vercel.com/docs/getting-started-with-vercel/import) from GitHub, GitLab, or Bitbucket. 155 | 2. Click on the environment variables and set `MICROCMS_API_KEY`, `MICROCMS_SERVICE_DOMAIN` and `BASE_URL`. 156 | 3. Click Deploy! 157 | 158 | #### **Deploy from Our Template** 159 | 160 | Alternatively, you can deploy our template by clicking on the Deploy button at the top of this page and setting the same environment variables. 161 | 162 | --- 163 | 164 | # Appendix 165 | 166 | ## Page Preview Settings 167 | 168 | In order to preview draft content, a page preview must be set up in the microCMS administration page. 169 | 170 | Set the following in "API Settings > Page Preview" of the Blog API. 171 | 172 | Replace `your-domain` with your deployed domain. (It also works with localhost) 173 | 174 | ![](public/page-preview-settings.png) 175 | 176 | Once set, the page preview button will be available on the content editing page. 177 | -------------------------------------------------------------------------------- /app/blogs/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from 'next'; 2 | import { getDetail } from '@/libs/microcms'; 3 | import Article from '@/components/Article'; 4 | 5 | type Props = { 6 | params: { 7 | slug: string; 8 | }; 9 | searchParams: { 10 | dk: string; 11 | }; 12 | }; 13 | 14 | export const revalidate = 60; 15 | 16 | export async function generateMetadata({ params, searchParams }: Props): Promise { 17 | const data = await getDetail(params.slug, { 18 | draftKey: searchParams.dk, 19 | }); 20 | 21 | return { 22 | title: data.title, 23 | description: data.description, 24 | openGraph: { 25 | title: data.title, 26 | description: data.description, 27 | images: [data?.thumbnail?.url || ''], 28 | }, 29 | }; 30 | } 31 | 32 | export default async function Page({ params, searchParams }: Props) { 33 | const data = await getDetail(params.slug, { 34 | draftKey: searchParams.dk, 35 | }); 36 | 37 | return
; 38 | } 39 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microcmsio/simple-blog-with-microcms/89c772ce93fac6e88829aff8f2397ab271be78ed/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', 'Roboto Mono', 3 | 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', 'Fira Mono', 'Droid Sans Mono', 4 | 'Courier New', monospace; 5 | --color-text-main: #333; 6 | --color-text-sub: #999; 7 | --color-bg-main: #fff; 8 | --color-bg-sub: #f3f3f3; 9 | --color-bg-code: #fafafa; 10 | --color-border-dark: #ccc; 11 | --color-border: #ddd; 12 | --color-border-light: #f3f3f3; 13 | --color-current: #eee; 14 | --border-radius: 4px; 15 | } 16 | 17 | * { 18 | box-sizing: border-box; 19 | padding: 0; 20 | margin: 0; 21 | } 22 | 23 | html, 24 | body { 25 | max-width: 100vw; 26 | overflow-x: hidden; 27 | } 28 | 29 | body { 30 | font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', YuGothic, 'ヒラギノ角ゴ ProN W3', 31 | Hiragino Kaku Gothic ProN, Arial, 'メイリオ', Meiryo, sans-serif; 32 | color: var(--color-text-main); 33 | line-height: 1.8; 34 | } 35 | 36 | code { 37 | font-family: menlo, inconsolata, monospace; 38 | } 39 | 40 | a { 41 | color: inherit; 42 | text-decoration: none; 43 | } 44 | 45 | ol, 46 | ul { 47 | list-style: none; 48 | } 49 | -------------------------------------------------------------------------------- /app/layout.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 720px; 3 | margin: 0 auto; 4 | } 5 | 6 | @media (max-width: 640px) { 7 | .main { 8 | width: auto; 9 | padding: 24px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { getTagList } from '@/libs/microcms'; 2 | import { LIMIT } from '@/constants'; 3 | import Header from '@/components/Header'; 4 | import Footer from '@/components/Footer'; 5 | import Nav from '@/components/Nav'; 6 | import './globals.css'; 7 | import styles from './layout.module.css'; 8 | 9 | export const metadata = { 10 | metadataBase: new URL(process.env.BASE_URL || 'http://localhost:3000'), 11 | title: 'Simple Blog', 12 | description: 'A simple blog presented by microCMS', 13 | openGraph: { 14 | title: 'Simple Blog', 15 | description: 'A simple blog presented by microCMS', 16 | images: '/ogp.png', 17 | }, 18 | alternates: { 19 | canonical: '/', 20 | }, 21 | }; 22 | 23 | type Props = { 24 | children: React.ReactNode; 25 | }; 26 | 27 | export default async function RootLayout({ children }: Props) { 28 | const tags = await getTagList({ 29 | limit: LIMIT, 30 | }); 31 | return ( 32 | 33 | 34 |
35 |