How to use nextjs with multiple plugins - reactjs

I'm using Nextjs with a with-antd boilerplate and it comes with a preconfigured next.config.js file.
Looking like this;
/* eslint-disable */
const withCss = require('#zeit/next-css')
// fix: prevents error when .css files are required by node
if (typeof require !== 'undefined') {
require.extensions['.css'] = (file) => {}
}
module.exports = withCss()
I want to edit this configuration file and add configurations like exportPathMap.
Like this:
module.exports = {
exportPathMap: function () {
return {
'/': { page: '/' },
'/about': { page: '/about' },
'/p/hello-nextjs': { page: '/post', query: { title: 'Hello Next.js' } },
'/p/learn-nextjs': { page: '/post', query: { title: 'Learn Next.js is awesome' } },
'/p/deploy-nextjs': { page: '/post', query: { title: 'Deploy apps with Zeit' } }
}
}
}
But I have no idea how to implement that without breaking the withCss plugin, please help.

Solved this by realizing that next-plugins such as #zeit/next-css that I was using expect more next configurations that are passed as objects from the plugin.
Snippet from #zeit/next-css plugin.
module.exports = (nextConfig = {}) => {
return Object.assign({}, nextConfig, {
webpack(config, options) {
if (!options.defaultLoaders) {
throw new Error(
'This plugin is not compatible with Next.js versions below 5.0.0 https://err.sh/next-plugins/upgrade'
)
}
So with that figured out I fixed the exportPathMap as an object inside withCss.
module.exports = withCss({
exportPathMap: function() {
return {
'/': {page: '/'},
'/sevices': {page: '/services'},
'/about': {page: '/about'},
'/contacts': {page: '/contacts'},
}
}
})
That's it! 🙌🙌

Related

Vercel - NextJS rewrites not working as expected in production

I have a website built by Next.js and we deployed it on Vercel.
We localize our website in 5 different languages, and as I found the solution with rewrites for localizing the urls as well.
We have a custom page: customer-stories, and would like to localize these:
customer_stories: {
hu: '/esettanulmanyok',
en: '/customer-stories',
de: '/kundengeschichte',
sk: '/pribehy-zakaznikov',
cs: '/pribehy-zakazniku',
},
So in the next config file:
Inside rewrites:
{
source: '/hu/esettanulmanyok/:id*/',
destination: '/hu/customer-stories/:id*/',
locale: false,
},
{
source: '/de/kundengeschichte/:id*/',
destination: '/de/customer-stories/:id*/',
locale: false,
},
{
source: '/cs/pribehy-zakazniku/:id*/',
destination: '/cs/customer-stories/:id*/',
locale: false,
},
{
source: '/sk/pribehy-zakaznikov/:id*/',
destination: '/sk/customer-stories/:id*/',
locale: false,
},
Which is working perfectly on localhost:
http://localhost:3000/hu/esettanulmanyok/
And a dynamic id page:
http://localhost:3000/hu/esettanulmanyok/newtestwithreference/
They work as expected, but on the released site it is different:
The same url is 404: https://barion-builderio.vercel.app/hu/esettanulmanyok/
The same dynamic url is working: https://barion-builderio.vercel.app/hu/esettanulmanyok/newtestwithreference/
What is very interested, because the main page is using pagination with getInitialProps:
When I go the original page without rewrites: https://barion-builderio.vercel.app/hu/customer-stories/
And after that, I paginate on the website:
And click on the next page:
The url is changing to the correct one, but if I try to refresh on the page, again 404:
So the dynamic [id] site is working well with rewrites, but the list page with getinitialprops not working for the first time, only with the original url, and after that if the user uses the pagination the rewrites will working. On the page we use shallow routing for not loading the site again.
I don't know why is it working properly on localhost and not on the server:
Here is the full (important part) code of my customer-stories.js:
imports...
const articlesPerPage = 4
const CustomerStories = ({
stories,
texts,
tophero,
bottomhero,
enumlocalization,
page,
navbar,
footer,
subfooter,
}) => {
const { locale, pathname } = useRouter()
const [currentPage, setCurrentPage] = useState(page)
const [filteredData, setFilteredData] = useState(stories)
const router = useRouter()
const handlePageReset = () => {
console.log('reset futott')
setCurrentPage(0)
handlePaginationClick(0)
}
const {
country,
industry,
solution,
handleIndustry,
handleCountry,
handleSolution,
} = useCustomerStoriesContext()
//FILTERING ON THE SITE
useDidMountEffect(async () => {
const queryArray = {}
if (solution && solution.key != null) {
queryArray = {
...queryArray,
'data.solution.$elemMatch.solution.type': solution.key.type,
}
}
if (industry && industry.key != null) {
queryArray = {
...queryArray,
'data.industry.type.$eq': industry.key.type,
}
}
if (country && country.key != null) {
queryArray = {
...queryArray,
'data.country.type.$eq': country.key.type,
}
}
const result = await builder.getAll('barion-userstory-data', {
options: {
noTargeting: true,
query: queryArray,
page: 0,
},
omit: 'data.blocks',
})
handlePageReset()
setFilteredData((prev) => result)
}, [country, industry, solution])
const handlePaginationClick = (page) => {
//setCurrentPage(page)
router.push(
{
pathname: LOCALIZED_URLS['customer_stories'][locale],
query: page != 0 ? { page: page != 0 ? page : '' } : {},
},
undefined,
{ shallow: false }
)
}
return (
<>
<Head>
<meta name="robots" content="noindex" />
</Head>
<div>
PAGE LIST CONTENT CUSTOMER-STORIES
</div>
</>
)
}
CustomerStories.getInitialProps = async ({
req,
res,
asPath,
query,
locale,
}) => {
if (locale == 'default') {
return {
props: {
plugins: null,
texts: null,
enumlocalization: null,
integrationready: null,
},
}
}
... FETCH DATA
const page = Number(query.page) || 0
let stories = await builder.getAll('barion-userstory-data', {
options: {
noTargeting: true,
// sort: { //TODO ADD SORTING ALPHABETICALLY
// 'data.title': 1,
// },
offset: page * articlesPerPage,
},
limit: articlesPerPage * 3,
omit: 'data.blocks',
})
...
return {
stories,
tophero,
bottomhero,
texts: texts?.data.texts[locale],
enumlocalization: enumlocalization.data,
page,
navbar,
footer,
subfooter,
}
}
export default CustomerStories

Error: `siteUrl` does not exist on `siteMetadata` in the data returned from the query. How to resolve?

When building my Gatsby website I get the below error.
I've tried to delete and reinstall npm, update the plugins, deleting (Gatsby) cache, playing around with the siteUrl and all kinds of settings in the Gatsby config. But I can't seem to get rid of the error. The development environment works fine.
github: https://github.com/bartfluitsma/gatsby-bart-fluitsma
**Error console log**
ERROR #11321 PLUGIN
"gatsby-plugin-sitemap" threw an error while running the onPostBuild lifecycle:
`siteUrl` does not exist on `siteMetadata` in the data returned from the query.
Add this to your `siteMetadata` object inside gatsby-config.js or add this to your custom query or provide a custom `resolveSiteUrl` function.
https://www.gatsbyjs.com/plugins/gatsby-plugin-sitemap/#api-reference
47 | errors = _yield$graphql.errors;
48 | _context.next = 9;
> 49 | return Promise.resolve(resolveSiteUrl(queryRecords)).catch(function (err) {
| ^
50 | return reporter.panic(_internals.REPORTER_PREFIX + " Error resolving Site URL", err);
51 | });
52 |
File: node_modules\gatsby-plugin-sitemap\gatsby-node.js:49:36
Error: `siteUrl` does not exist on `siteMetadata` in the data returned from the query.
Add this to your `siteMetadata` object inside gatsby-config.js or add this to your custom query or provide a custom `resolveSiteUrl` function.
https://www.gatsbyjs.com/plugins/gatsby-plugin-sitemap/#api-reference
- internals.js:62 resolveSiteUrl
[gatsby-bart-fluitsma]/[gatsby-plugin-sitemap]/internals.js:62:11
- gatsby-node.js:49 _callee$
[gatsby-bart-fluitsma]/[gatsby-plugin-sitemap]/gatsby-node.js:49:36
not finished onPostBuild - 0.480s
Gatsby-config.js
module.exports = {
siteMetadata: {
title: `Bart Fluitsma`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `#gatsbyjs`,
siteUrl: `http://bartfluitsma.com`,
},
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-plugin-image`,
'gatsby-plugin-postcss',
{
resolve: `gatsby-plugin-google-fonts-with-attributes`,
options: {
fonts: [
`montserrat\:300,400,400i,600,900`,
],
display: 'swap',
attributes: {
rel: "stylesheet preload prefetch",
},
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
}, {
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/locales`,
name: `locale`
}
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Web development | Bart Fluitsma`,
short_name: `Bart develops`,
start_url: `/`,
background_color: `#663399`,
// This will impact how browsers show your PWA/website
// https://css-tricks.com/meta-theme-color-and-trickery/
// theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/logo-bart-fluitsma-web-design.svg`, // This path is relative to the root of the site.
},
},
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`, // name given to `gatsby-source-filesystem` plugin.
languages: [`en`, `nl`],
defaultLanguage: `en`,
// if you are using Helmet, you must include siteUrl, and make sure you add http:https
siteUrl: `https://bartfluitsma.com`,
// you can pass any i18next options
i18nextOptions: {
interpolation: {
escapeValue: false // not needed for react as it escapes by default
},
keySeparator: false,
nsSeparator: false
},
pages: [
{
matchPath: '/:lang?/blog/:uid',
getLanguageFromPath: true
},
]
}
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// `gatsby-plugin-offline`,
{
resolve: 'gatsby-plugin-sitemap',
options: {
excludes: ['/**/404', '/**/404.html'],
query: `
{
site {
siteMetadata {
siteUrl
}
}
allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
edges {
node {
context {
i18n {
defaultLanguage
languages
originalPath
}
}
path
}
}
}
}
`,
serialize: ({ site, allSitePage }) => {
return allSitePage.edges.map((edge) => {
const { languages, originalPath, defaultLanguage } = edge.node.context.i18n;
const { siteUrl } = site.siteMetadata;
const url = siteUrl + originalPath;
const links = [
{ lang: defaultLanguage, url },
{ lang: 'x-default', url }
];
languages.forEach((lang) => {
if (lang === defaultLanguage) return;
links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` });
});
return {
url,
changefreq: 'daily',
priority: originalPath === '/' ? 1.0 : 0.7,
links
};
});
}
}
},
],
}
The error is thrown by gatsby-plugin-sitemap. Try adding the resolveSiteUrl method in your configuration like:
const siteUrl = process.env.URL || `https://fallback.net`
resolveSiteUrl: () => siteUrl,
Applied to your code:
const siteUrl = process.env.URL || `https://fallback.net`
module.exports = {
plugins: [
{
resolve: "gatsby-plugin-sitemap",
options: {
excludes: ["/**/404", "/**/404.html"],
query: `
{
site {
siteMetadata {
siteUrl
}
}
allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
edges {
node {
context {
i18n {
defaultLanguage
languages
originalPath
}
}
path
}
}
}
}
`,
resolveSiteUrl: () => siteUrl,
serialize: ({ site, allSitePage }) => {
return allSitePage.edges.map((edge) => {
const { languages, originalPath, defaultLanguage } =
edge.node.context.i18n;
const { siteUrl } = site.siteMetadata;
const url = siteUrl + originalPath;
const links = [
{ lang: defaultLanguage, url },
{ lang: "x-default", url },
];
languages.forEach((lang) => {
if (lang === defaultLanguage) return;
links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` });
});
return {
url,
changefreq: "daily",
priority: originalPath === "/" ? 1.0 : 0.7,
links,
};
});
},
},
},
],
};
Change the siteUrl and the fallback URL accordingly if you are not setting it in your environment file
The answer by Ferran Buireu ultimately was not the solution for OP. I had experienced the same issue and this solution would have solved his issue hadn't he abandoned it. Check this GitHub thread.
Your siteUrl issue just masks that the query was invalid, as the context is not available in gatsby >= 4 anymore, as you found out after fixing the siteUrl.
You may have used this query from the gatsby-plugin-react-i18next docs to support a sitemap.
In order to make it work, I found you have to 1. create the context yourself, and 2. adjust the queries
Create your context through gatsby-node.js (credit wilsonvolker)
/**
* Workaround for missing sitePage.context:
* Used for generating sitemap with `gatsby-plugin-react-i18next` and `gatsby-plugin-sitemap` plugins
* https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v3-to-v4/#field-sitepagecontext-is-no-longer-available-in-graphql-queries
*/
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type SitePage implements Node {
context: SitePageContext
}
type SitePageContext {
i18n: i18nContext
}
type i18nContext {
language: String,
languages: [String],
defaultLanguage: String,
originalPath: String
routed: Boolean
}
`)
}
It looks like the serialize function proposed in the i18next doesn't work as-is anymore since it apparently receives a single node, not the full graphql response. So, a few changes in gatsby-config.js to make it work again (this assumes you have a global siteUrl variable available):
query: `
{
site {
siteMetadata {
siteUrl
}
}
allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
nodes {
context {
i18n {
defaultLanguage
languages
originalPath
}
}
path
}
}
}
`,
serialize: (node) => {
const { languages, originalPath, defaultLanguage } = node.context.i18n
const url = siteUrl + originalPath
const links = [
{ lang: defaultLanguage, url },
{ lang: 'x-default', url },
]
languages.forEach((lang) => {
if (lang === defaultLanguage) return
links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` })
})
return {
url,
changefreq: 'daily',
priority: originalPath === '/' ? 1.0 : 0.7,
links,
}
},
This worked for me without the need to create new types:
{
resolve: "gatsby-plugin-sitemap",
options: {
excludes: ["/**/404", "/**/404.html"],
resolveSiteUrl: () => siteUrl,
query: `
{
allSitePage {
edges {
node {
pageContext
}
}
}
}
`,
resolvePages: ({ allSitePage: { edges } }) => {
return edges
.filter(
({ node }) => !["/404/", "/404.html"].includes(node.pageContext.i18n.originalPath)
)
.map(({ node: { pageContext } }) => {
const { languages, originalPath, path, defaultLanguage } = pageContext.i18n;
const baseUrl = siteUrl + originalPath;
const links = [{ lang: "x-default", url: baseUrl }];
languages.forEach((lang) => {
const isDefaultLang = lang === defaultLanguage;
const isDefaultPath = path === originalPath;
const isLangSubDir = path.includes(`${lang}/`);
if (isDefaultLang && isDefaultPath) return;
if (!isDefaultLang && isLangSubDir) return;
links.push({
lang,
url: isDefaultLang ? baseUrl : `${siteUrl}/${lang}${originalPath}`,
});
});
return {
path,
url: path === "/" ? siteUrl : `${siteUrl}/${path}`,
changefreq: "daily",
priority: originalPath === "/" ? 1.0 : 0.7,
links,
};
});
},
serialize: (page) => page,
},
}
Versions:
Node: 18.13.0
"gatsby": "^5.3.3",
"gatsby-plugin-react-i18next": "^3.0.1",
"gatsby-plugin-sitemap": "^6.5.0",

WebpackError: TypeError: Cannot destructure property

When I try gatsby develop the application works fine. However, when I try gatsby build, I get the following error:
This is my query (I tested it in GraphQL, and it returns the values correctly)
I'm using gatsby-plugin-mdx.
I don't know what to do. Have someone an idea how to solve it?
One of the things that you're missing from your createPosts function inside gatsby-node.js is the path for your blog post so that you can use it in the GraphQL query inside blogTemplate.js.
Also, it looks like path is a reserved property, so you might want to use something else.
// gatsby-node.js
const createPosts = (posts, createPage, blogTempate) => {
posts.forEach(({ node }, index) => {
const path = node.frontmatter.path
createPage({
path,
component: blogTemplate,
context: {
blogPath: path,
...
}
})
})
}
Then, change your query to the following:
// blogTemplate.js
const BlogPost = ({ data }) => {
// ...
}
export const query = graphql`
query($blogPath: String) {
mdx(frontmatter: { path: { eq: $blogPath } }) {
...
}
}
`
Finally, change gatsby-config.js so that it looks like the following:
module.exports = {
plugins: [
`gatsby-plugin-mdx`,
{ resolve: `gatsby-source-filesystem`, ... },
{ resolve: `gatsby-plugin-page-creator`, ... }
]
}
Hope this helps.

While trying to create pages in gatsby-node, I received this build error

ERROR #11321 PLUGIN
"gatsby-node.js" threw an error while running the onCreateNode lifecycle:
Cannot read property 'replace' of undefined
25 | const { createNodeField } = actions;
26 | if (node.internal.type === 'MarkdownRemark') {
> 27 | const slug = createFilePath({ node, getNode, basePath: 'content' });
| ^
28 | createNodeField({
29 | node,
30 | name: 'slug',
File: gatsby-node.js:27:18
TypeError: Cannot read property 'replace' of undefined
- path.js:54 slash
[netcreative0]/[gatsby-core-utils]/dist/path.js:54:15
- create-file-path.js:41 module.exports
[netcreative0]/[gatsby-source-filesystem]/create-file-path.js:41:61
- gatsby-node.js:27 Object.exports.onCreateNode
C:/code/netcreative0/gatsby-node.js:27:18
- api-runner-node.js:247 runAPI
[netcreative0]/[gatsby]/dist/utils/api-runner-node.js:247:41
- api-runner-node.js:387 resolve
[netcreative0]/[gatsby]/dist/utils/api-runner-node.js:387:13
- new Promise
- api-runner-node.js:386 runPlugin
[netcreative0]/[gatsby]/dist/utils/api-runner-node.js:386:10
- api-runner-node.js:340 module.exports
[netcreative0]/[gatsby]/dist/utils/api-runner-node.js:340:24
- next_tick.js:68 process._tickCallback
internal/process/next_tick.js:68:7
Here's all the code in that file. I found this link https://github.com/datocms/gatsby-source-datocms/issues/52 but it didn't seem to help to remove getNode. in the path plugin, is this slash. I noticed that the path is undefined when I console.log it but I don't know what to do about it.
**function slash(path) {
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
if (isExtendedLengthPath) {
return path;
}
return path.replace(/\\/g, `/`);
}**
const path = require('path');
const DirectoryNamedWebpackPlugin = require('directory-named-webpack-plugin');
const { createFilePath } = require('gatsby-source-filesystem');
exports.onCreateWebpackConfig = ({
stage,
getConfig,
rules,
loaders,
actions,
}) => {
actions.setWebpackConfig({
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
plugins: [
new DirectoryNamedWebpackPlugin({
exclude: /node_modules/,
}),
],
},
});
};
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === 'MarkdownRemark') {
const slug = createFilePath({ node, getNode, basePath: 'slugs' });
console.log(slug);
createNodeField({
node,
name: `slug`,
value: slug,
});
}
};
the second half of my code that looks like:
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions;
const result = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`)
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
console.log(node)
createPage({
path: node.fields ? node.fields.slug : '',
component: path.resolve(`./src/template/project.js`),
context: {
// Data passed to context is available
// in page queries as GraphQL variables.
slug: node.fields ? node.fields.slug : '',
},
})
});
}
The last field is null and throws an error. Not sure why it's even there? I could easily put in a condition but what would the default be? Any suggestions? See below
{ fields: { slug: '/about/content/' } }
{ fields: { slug: '/home/intro/' } }
{ fields: { slug: '/slugs/goldparent/' } }
{ fields: { slug: '/slugs/ipadmaster/' } }
{ fields: null }
After removing the contentful plugin, I was able to see a more useful error message.
Some images were missing from the .md files when creating new pages so an error appeared on this line in my template page
data.frontmatter.featuredImage.childImageSharp.fluid <-
I added a ternary condition to the image tag and it built after that
<Img
style={{ width: '70%' }}
fluid={
data.frontmatter.featuredImage
? data.frontmatter.featuredImage.childImageSharp.fluid
: {}
}
alt="Large Image"
/>

ReactNative Expo Preloading & Caching Images

I'm new to react-native im trying to preload 10 images at the start of the app I followed expo documentation but
I want to cache images from an external file but it gives me an error [Un Handeled Promise Rejection]
here is my entries.js
export const ENTRIES1 = [
{
title: 'Makeup Artists',
illustration: require('../assets/img/makeup.png')
},
{
title: 'Photographers',
illustration: require('../assets/img/Photographers.png')
},
{
title: 'Wedding Planners',
illustration: require('../assets/img/weddingPlanner.jpg')
},
{
title: 'Wedding Halls',
illustration: require('../assets/img/wedding-Hall.png')
},
{
title: 'Laser & Beauty Centers',
illustration: require('../assets/img/laser.png')
},
]
loadingScreen.js
async componentDidMount() { //Preload Fonts
await Asset.loadAsync(ENTRIES1.illustration),
await Font.loadAsync({
'Roboto': require('../../node_modules/native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('../../node_modules/native-base/Fonts/Roboto_medium.ttf'),
...Ionicons.font,
});
this.checkIfLoggedIn();
}
what am i doing wrong ? Thanks
Try this :)
function cacheImages(images) {
return images.map(image => {
if (typeof image.illustration === 'string') {
return Image.prefetch(image.illustration);
} else {
return Asset.fromModule(image.illustration).downloadAsync();
}
});
}
async componentDidMount() {
await Asset.cacheImages(ENTRIES1),
await Font.loadAsync({
'Roboto': require('../../node_modules/native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('../../node_modules/native-base/Fonts/Roboto_medium.ttf'),
...Ionicons.font,
});
this.checkIfLoggedIn();
}

Resources