Create a Preview Button in Strapi v3 for Next.js

Customization

http://localhost:3000/api/preview?secret=fdsfasdmgrNPQXtfdsfMswfdsfdsfasdkjfow&slug=pricing](http://localhost:3000/api/preview?secret=fdsfasdmgrNPQXtfdsfMswfdsfdsfasdkjfow&slug=pricing&lang=en&type=page

Extensions

  • Create a ./extensions/content-manager/admin/src/index.js file containing the following:
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import pluginLogo from './assets/images/logo.svg';
import App from './containers/Main';
import ExternalLink from './InjectedComponents/ExternalLink';
import ConfigureViewButton from './InjectedComponents/ContentTypeBuilder/ConfigureViewButton';
import lifecycles from './lifecycles';
import reducers from './reducers';
import trads from './translations';
export default (strapi) => {
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const plugin = {
blockerComponent: null,
blockerComponentProps: {},
description: pluginDescription,
icon: pluginPkg.strapi.icon,
id: pluginId,
initializer: null,
injectedComponents: [
{
plugin: 'content-type-builder.listView',
area: 'list.link',
component: ConfigureViewButton,
key: 'content-manager.link',
},
// This is the injection zone
{
plugin: 'content-manager.editView',
area: 'right.links',
component: ExternalLink,
key: 'content-manager.preview-link',
},
],
injectionZones: {
editView: { informations: [] },
listView: { actions: [], deleteModalAdditionalInfos: [] },
},
isReady: true,
isRequired: pluginPkg.strapi.required || false,
layout: null,
lifecycles,
mainComponent: App,
name: pluginPkg.strapi.name,
pluginLogo,
preventComponentRendering: false,
reducers,
trads,
};
return strapi.registerPlugin(plugin);
};
import ExternalLink from './InjectedComponents/ExternalLink';
injectedComponents: [
{
plugin: 'content-type-builder.listView',
area: 'list.link',
component: ConfigureViewButton,
key: 'content-manager.link',
},
// This is the injection zone
{
plugin: 'content-manager.editView',
area: 'right.links',
component: ExternalLink,
key: 'content-manager.preview-link',
},
],
  • Create a ./extensions/content-manager/admin/src/InjectedComponents/ExternalLink/index.js file containing the following:
import React from 'react';
import styled from 'styled-components';
import { useContentManagerEditViewDataManager } from 'strapi-helper-plugin';
import EyeIcon from './view.svg';
const StyledExternalLink = styled.a`
display: block;
color: #333740;
width: 100%;
text-decoration: none;
span,
i,
svg {
color: #333740;
width: 13px;
height: 12px;
margin-right: 10px;
vertical-align: 0;
}
span {
font-size: 13px;
}
i {
display: inline-block;
background-image: url(${EyeIcon});
background-size: contain;
}
&:hover {
text-decoration: none;
span,
i,
svg {
color: #007eff;
}
}
`;
const ExternalLink = () => {
const { modifiedData, layout } = useContentManagerEditViewDataManager();

if (modifiedData.published_at) {
return null;
}
if (!modifiedData.slug) {
return null;
}
if (!CLIENT_URL || !CLIENT_PREVIEW_SECRET) {
return null;
}
return (
<li>
<StyledExternalLink
href={`${CLIENT_URL}/api/preview?secret=${CLIENT_PREVIEW_SECRET}&slug=${modifiedData.slug}&locale=${modifiedData.locale}&apiID=${layout.apiID}&kind=${layout.kind}`}
target="_blank"
rel="noopener noreferrer"
title="page preview"
>
<i />
Preview
</StyledExternalLink>
</li>
);
};
export default ExternalLink;
  • Create a ./extensions/content-manager/admin/src/InjectedComponents/ExternalLink/view.svg file containing the following:
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
const ExternalLink = () => {
const { modifiedData, layout } = useContentManagerEditViewDataManager();

if (modifiedData.published_at) {
return null;
}
if (!modifiedData.slug) {
return null;
}
if (!CLIENT_URL || !CLIENT_PREVIEW_SECRET) {
return null;
}
return (
<li>
<StyledExternalLink
href={`${CLIENT_URL}/api/preview?secret=${CLIENT_PREVIEW_SECRET}&slug=${modifiedData.slug}&locale=${modifiedData.locale}&apiID=${layout.apiID}&kind=${layout.kind}`}
target="_blank"
rel="noopener noreferrer"
title="page preview"
>
<i />
Preview
</StyledExternalLink>
</li>
);
};
if (modifiedData.published_at) {
return null;
}
if (!modifiedData.slug) {
return null;
}
if (!CLIENT_URL || !CLIENT_PREVIEW_SECRET) {
return null;
}
return (
<li>
<StyledExternalLink
href={`${CLIENT_URL}/api/preview?secret=${CLIENT_PREVIEW_SECRET}&slug=${modifiedData.slug}&locale=${modifiedData.locale}&apiID=${layout.apiID}&kind=${layout.kind}`}
target="_blank"
rel="noopener noreferrer"
title="page preview"
>
<i />
Preview
</StyledExternalLink>
</li>
);
`${CLIENT_URL}/api/preview?secret=${CLIENT_PREVIEW_SECRET}&slug=${modifiedData.slug}`
// ./pages/api/preview.jsimport { getData } from '../../utils';export default async (req, res) => {
if (
req.query.secret !== process.env.PREVIEW_SECRET ||
(req.query.slug != '' && !req.query.slug)
) {
return res.status(401).json({ message: 'Invalid token' });
}
const previewData = await getData(
req.query.slug,
req.query.locale,
req.query.apiID,
req.query.kind,
null
);
if (!previewData.data) {
return res.status(401).json({ message: 'Invalid slug' });
}
res.setPreviewData({});
res.writeHead(307, {
Location: previewData.slug,
});
res.end();
};
`${CLIENT_URL}/api/preview?secret=${CLIENT_PREVIEW_SECRET}&slug=${modifiedData.slug}&locale=${modifiedData.locale}&apiID=${layout.apiID}&kind=${layout.kind}`
  • Build your admin with the following command:
yarn build --clean
  • Restart your server
yarn develop

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Strapi

Strapi

The open source Headless CMS Front-End Developers love.