Add a Plasmic example (#37522)
This adds a new example under `cms-plasmic/`. It serves as a general-purpose example that should be able to work with any Plasmic project, which can be set via environment variables. ## Documentation / Examples - [x] Make sure the linting passes by running `pnpm lint` - [x] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples)
This commit is contained in:
parent
cf3ba271e9
commit
ee89517848
15 changed files with 370 additions and 0 deletions
|
@ -24,6 +24,7 @@ description: Next.js has the preview mode for statically generated pages. You ca
|
|||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms">GraphCMS Example</a> (<a href="https://next-blog-graphcms.vercel.app/">Demo</a>)</li>
|
||||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-kontent">Kontent Example</a> (<a href="https://next-blog-kontent.vercel.app//">Demo</a>)</li>
|
||||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-umbraco-heartcore">Umbraco Heartcore Example</a> (<a href="https://next-blog-umbraco-heartcore.vercel.app/">Demo</a>)</li>
|
||||
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/cms-plasmic">Plasmic Example</a> (<a href="https://nextjs-plasmic-example.vercel.app/">Demo</a>)</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
|
|
3
examples/cms-plasmic/.env.local.example
Normal file
3
examples/cms-plasmic/.env.local.example
Normal file
|
@ -0,0 +1,3 @@
|
|||
NEXT_PUBLIC_PLASMIC_PROJECT_ID=
|
||||
NEXT_PUBLIC_PLASMIC_PROJECT_API_TOKEN=
|
||||
PLASMIC_PREVIEW_SECRET=
|
32
examples/cms-plasmic/.gitignore
vendored
Normal file
32
examples/cms-plasmic/.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
110
examples/cms-plasmic/README.md
Normal file
110
examples/cms-plasmic/README.md
Normal file
|
@ -0,0 +1,110 @@
|
|||
# A statically generated landing page using Next.js and Plasmic
|
||||
|
||||
This example showcases Next.js's [Static Generation](https://nextjs.org/docs/basic-features/pages) feature using [Plasmic](https://www.plasmic.app/) as the visual page builder.
|
||||
|
||||
You'll get:
|
||||
|
||||
- Statically generated pages from your visual designs
|
||||
- Development server on [preview mode](https://nextjs.org/docs/advanced-features/preview-mode) watches for changes from Plasmic Studio
|
||||
|
||||
## Demo
|
||||
|
||||
### [https://nextjs-plasmic-example.vercel.app/](https://nextjs-plasmic-example.vercel.app/)
|
||||
|
||||
## Deploy your own
|
||||
|
||||
Once you have access to the [environment variables you need](#step-3-set-up-environment-variables), deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
|
||||
|
||||
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fcms-plasmic&env=NEXT_PUBLIC_PLASMIC_PROJECT_ID,NEXT_PUBLIC_PLASMIC_PROJECT_API_TOKEN,PLASMIC_PREVIEW_SECRET&envDescription=Required%20to%20connect%20the%20app%20with%20Plasmic&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fcms-plasmic)
|
||||
|
||||
## How to use
|
||||
|
||||
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
|
||||
|
||||
```bash
|
||||
npx create-next-app --example cms-plasmic cms-plasmic-app
|
||||
# or
|
||||
yarn create next-app --example cms-plasmic cms-plasmic-app
|
||||
# or
|
||||
pnpm create next-app --example cms-plasmic cms-plasmic-app
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Step 1. Create an account and a project on Plasmic
|
||||
|
||||
First, [create an account on Plasmic](https://studio.plasmic.app/).
|
||||
|
||||
After creating an account, create a new project.
|
||||
|
||||
### Step 2. Gather your project ID and API token
|
||||
|
||||
Once you've opened your Plasmic project, you can find the project ID in the URL: `https://studio.plasmic.app/projects/PROJECTID`.
|
||||
|
||||
The API token can be found by clicking the Code button in the top bar.
|
||||
![api token](https://www.plasmic.app/blog/static/images/plasmicflix/08-api-token.png)
|
||||
|
||||
### Step 3. Set up environment variables
|
||||
|
||||
Copy the `.env.local.example` file in this directory to `.env.local` (which will be ignored by Git):
|
||||
|
||||
```bash
|
||||
cp .env.local.example .env.local
|
||||
```
|
||||
|
||||
Then set each variable on `.env.local`:
|
||||
|
||||
- `NEXT_PUBLIC_PLASMIC_PROJECT_ID` should be the `projectId` value in step 2.
|
||||
- `NEXT_PUBLIC_PLASMIC_PROJECT_API_TOKEN` should be the API token gathered in previous step.
|
||||
- `PLASMIC_PREVIEW_SECRET` can be any random string (but avoid spaces), like `MY_SECRET` - this is used for [Preview Mode](https://nextjs.org/docs/advanced-features/preview-mode).
|
||||
|
||||
### Step 4. Run Next.js in development mode
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
# or
|
||||
yarn install
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Your blog should be up and running on [http://localhost:3000](http://localhost:3000)! If it doesn't work, post on [GitHub discussions](https://github.com/vercel/next.js/discussions).
|
||||
|
||||
### Step 5. Try preview mode
|
||||
|
||||
By default, the code is set up to only build published Plasmic projects.
|
||||
If you want to see changes as you make them in the Plasmic Studio, enter preview mode by opening the following URL:
|
||||
|
||||
```
|
||||
http://localhost:3000/api/preview?secret=PLASMIC_PREVIEW_SECRET&slug=PATH
|
||||
```
|
||||
|
||||
Be sure to replace the secret with the chosen secret in Step 3 and pick a path to preview (e.g. `http://localhost:3000/api/preview?secret=123456&slug=/`)
|
||||
|
||||
Now you can make edits in the Studio and see them reflected in the development server live.
|
||||
|
||||
You can exit preview mode at any time by going to the following URL:
|
||||
|
||||
```
|
||||
http://localhost:3000/api/exit-preview
|
||||
```
|
||||
|
||||
### Step 6. Deploy on Vercel
|
||||
|
||||
You can deploy this app to the cloud with [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
|
||||
|
||||
To deploy your local project to Vercel, push it to GitHub/GitLab/Bitbucket and [import to Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example).
|
||||
|
||||
**Important**: When you import your project on Vercel, make sure to click on **Environment Variables** and set them to match your `.env.local` file.
|
||||
|
||||
## Next steps:
|
||||
|
||||
With Plasmic, you can enable non-developers on your team to publish pages and content into your website or app.
|
||||
|
||||
To learn more about Plasmic, take a look at the following resources:
|
||||
|
||||
- [Plasmic Website](https://www.plasmic.app/)
|
||||
- [Plasmic Documentation](https://docs.plasmic.app/learn/)
|
||||
- [Plasmic Slack Community](https://www.plasmic.app/slack)
|
||||
|
||||
You can check out [the Plasmic GitHub repository](https://github.com/plasmicapp/plasmic) - your feedback and contributions are welcome!
|
5
examples/cms-plasmic/next-env.d.ts
vendored
Normal file
5
examples/cms-plasmic/next-env.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
6
examples/cms-plasmic/next.config.js
Normal file
6
examples/cms-plasmic/next.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
19
examples/cms-plasmic/package.json
Normal file
19
examples/cms-plasmic/package.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@plasmicapp/loader-nextjs": "1.0.136",
|
||||
"next": "latest",
|
||||
"react": "18.1.0",
|
||||
"react-dom": "18.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "17.0.41",
|
||||
"@types/react": "18.0.12",
|
||||
"typescript": "4.7.3"
|
||||
}
|
||||
}
|
90
examples/cms-plasmic/pages/[[...catchall]].tsx
Normal file
90
examples/cms-plasmic/pages/[[...catchall]].tsx
Normal file
|
@ -0,0 +1,90 @@
|
|||
import {
|
||||
PlasmicComponent,
|
||||
ComponentRenderData,
|
||||
PlasmicRootProvider,
|
||||
extractPlasmicQueryData,
|
||||
} from '@plasmicapp/loader-nextjs'
|
||||
import type { GetStaticPaths, GetStaticProps } from 'next'
|
||||
import Error from 'next/error'
|
||||
import { PLASMIC, PREVIEW_PLASMIC } from '../plasmic-init'
|
||||
|
||||
/**
|
||||
* Use fetchPages() to fetch list of pages that have been created in Plasmic
|
||||
*/
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const pages = await PLASMIC.fetchPages()
|
||||
return {
|
||||
paths: pages.map((page) => ({
|
||||
params: { catchall: page.path.substring(1).split('/') },
|
||||
})),
|
||||
fallback: 'blocking',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For each page, pre-fetch the data we need to render it
|
||||
*/
|
||||
export const getStaticProps: GetStaticProps = async (context) => {
|
||||
const { catchall } = context.params ?? {}
|
||||
|
||||
// Convert the catchall param into a path string
|
||||
const plasmicPath =
|
||||
typeof catchall === 'string'
|
||||
? catchall
|
||||
: Array.isArray(catchall)
|
||||
? `/${catchall.join('/')}`
|
||||
: '/'
|
||||
const plasmicData = await PLASMIC.maybeFetchComponentData(plasmicPath)
|
||||
if (!plasmicData) {
|
||||
// This is some non-Plasmic catch-all page
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
// This is a path that Plasmic knows about.
|
||||
// Cache the necessary data fetched for the page.
|
||||
const queryCache = await extractPlasmicQueryData(
|
||||
<PlasmicRootProvider loader={PLASMIC} prefetchedData={plasmicData}>
|
||||
<PlasmicComponent component={plasmicData.entryCompMetas[0].displayName} />
|
||||
</PlasmicRootProvider>
|
||||
)
|
||||
|
||||
// Pass the data in as props.
|
||||
return {
|
||||
props: { plasmicData, queryCache, preview: context.preview ?? null },
|
||||
|
||||
// Using incremental static regeneration, will invalidate this page
|
||||
// after 300s (no deploy webhooks needed)
|
||||
revalidate: 300,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually render the page!
|
||||
*/
|
||||
export default function CatchallPage(props: {
|
||||
plasmicData?: ComponentRenderData
|
||||
queryCache?: Record<string, any>
|
||||
preview?: boolean
|
||||
}) {
|
||||
const { plasmicData, queryCache, preview } = props
|
||||
if (!plasmicData || plasmicData.entryCompMetas.length === 0) {
|
||||
return <Error statusCode={404} />
|
||||
}
|
||||
const pageMeta = plasmicData.entryCompMetas[0]
|
||||
return (
|
||||
// Pass in the data fetched in getStaticProps as prefetchedData
|
||||
<PlasmicRootProvider
|
||||
loader={preview ? PREVIEW_PLASMIC : PLASMIC}
|
||||
prefetchedData={preview ? undefined : plasmicData}
|
||||
prefetchedQueryData={preview ? undefined : queryCache}
|
||||
>
|
||||
{
|
||||
// plasmicData.entryCompMetas[0].displayName contains the name
|
||||
// of the component you fetched.
|
||||
}
|
||||
<PlasmicComponent component={pageMeta.displayName} />
|
||||
</PlasmicRootProvider>
|
||||
)
|
||||
}
|
7
examples/cms-plasmic/pages/_app.js
Normal file
7
examples/cms-plasmic/pages/_app.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import '../styles/globals.css'
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
|
||||
export default MyApp
|
8
examples/cms-plasmic/pages/api/exit-preview.ts
Normal file
8
examples/cms-plasmic/pages/api/exit-preview.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
export default async function exit(_, res) {
|
||||
// Exit the current user from "Preview Mode". This function accepts no args.
|
||||
res.clearPreviewData()
|
||||
|
||||
// Redirect the user back to the index page.
|
||||
res.writeHead(307, { Location: '/' })
|
||||
res.end()
|
||||
}
|
28
examples/cms-plasmic/pages/api/preview.ts
Normal file
28
examples/cms-plasmic/pages/api/preview.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { PREVIEW_PLASMIC } from '../../plasmic-init'
|
||||
|
||||
export default async function preview(req, res) {
|
||||
// Check the secret and next parameters
|
||||
// This secret should only be known to this API route and the CMS
|
||||
if (
|
||||
req.query.secret !== process.env.PLASMIC_PREVIEW_SECRET ||
|
||||
!req.query.slug
|
||||
) {
|
||||
return res.status(401).json({ message: 'Invalid token' })
|
||||
}
|
||||
|
||||
// Check if the page with the given `slug` exists
|
||||
const pages = await PREVIEW_PLASMIC.fetchPages()
|
||||
const pageMeta = pages.find((p) => p.path === req.query.slug)
|
||||
|
||||
// If the slug doesn't exist prevent preview mode from being enabled
|
||||
if (!pageMeta) {
|
||||
return res.status(401).json({ message: 'Invalid slug' })
|
||||
}
|
||||
|
||||
// Enable Preview Mode by setting the cookies
|
||||
res.setPreviewData({})
|
||||
|
||||
// Redirect to the path from the fetched post
|
||||
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
|
||||
res.redirect(pageMeta.path)
|
||||
}
|
26
examples/cms-plasmic/plasmic-init.ts
Normal file
26
examples/cms-plasmic/plasmic-init.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { initPlasmicLoader } from '@plasmicapp/loader-nextjs'
|
||||
|
||||
const PLASMIC_PROJECT_ID = process.env['NEXT_PUBLIC_PLASMIC_PROJECT_ID']
|
||||
const PLASMIC_PROJECT_API_TOKEN =
|
||||
process.env['NEXT_PUBLIC_PLASMIC_PROJECT_API_TOKEN']
|
||||
|
||||
const PLASMIC_CONFIG = {
|
||||
projects: [
|
||||
{
|
||||
id: PLASMIC_PROJECT_ID,
|
||||
token: PLASMIC_PROJECT_API_TOKEN,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export const PLASMIC = initPlasmicLoader({
|
||||
...PLASMIC_CONFIG,
|
||||
preview: false,
|
||||
})
|
||||
|
||||
export const PREVIEW_PLASMIC = initPlasmicLoader({
|
||||
...PLASMIC_CONFIG,
|
||||
// Fetches the latest revisions, whether or not they were unpublished!
|
||||
// Disable for production to ensure you render only published changes.
|
||||
preview: true,
|
||||
})
|
BIN
examples/cms-plasmic/public/favicon.ico
Normal file
BIN
examples/cms-plasmic/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
16
examples/cms-plasmic/styles/globals.css
Normal file
16
examples/cms-plasmic/styles/globals.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
19
examples/cms-plasmic/tsconfig.json
Normal file
19
examples/cms-plasmic/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Loading…
Reference in a new issue