I have a blog run on Gatsby, and every time I push and deploy new blog posts I need to do a refresh on my page to see the new data.
I tried following the suggestions from this post and adding an onServiceWorkerUpdate function but that doesn't seem to have done anything.
Anyone have workarounds to this issue, and if so will there be a way to test locally? Changes already automatically update when I test in gatsby develop mode.
This is the entirety of my gatsby-browser.js file
export const onServiceWorkerUpdateReady = () => window.location.reload();
You need to install gatsby-plugin-offline first. Leaving your gatsby-config.js with something similar to:
{
plugins: [
{
resolve: `gatsby-plugin-manifest`,
options: {
...
}
},
'gatsby-plugin-offline'
]
}
Note: plugin's order matters in this case.
The plugin will register a service worker and will load it into the client.
Then, in your gatsby-browser.js file you can simply add:
export const onServiceWorkerUpdateReady = () => {
const answer = window.confirm(
`This application has been updated. ` +
`Reload to display the latest version?`
)
if (answer === true) window.location.reload()
}
Additionally, you may want to add the following hack (very common across the repositories):
export const onRouteUpdate = () => {
navigator.serviceWorker.register('/sw.js').then((reg) => {
reg.update();
});
};
Basically, it forces the registration of the service-worker across the site upon the onServiceWorkerUpdateReady refresh.
Check this interesting thread for further caveats and workarounds for some specific scenarios: https://github.com/gatsbyjs/gatsby/issues/9087#issuecomment-774680408
It will not work using Link provided by gatsby
import { Link as GatsbyLink } from 'gatsby';
<GatsbyLink to={to} {...props} />
It works using traditional a tag:
<a href={props.to} target="_blank">
{props.children}
</a>
Related
I'm fairly new with Javascript/Typescript and was developing a Single-Spa with Single SPA Router
Searching for info about registering applications I found different ways of doing that, one way is using the LayoutEngine that "automatically" register my applications this way:
const routes = constructRoutes(microfrontendLayout);
const applications = constructApplications({routes, loadApp({name}) {
return System.import(name);
}});
const layoutEngine = constructLayoutEngine({ routes, applications });
applications.forEach(registerApplication);
layoutEngine.activate();
From what I understood is, in microfrontend-layout.html we have to put something like an HTML tag in this way
<application name="#prueba-organizacion/micro-prueba-app1"></application>
with that and having an importmap in index.ejs the app was loading fine.
But then I found another way, the manually way:
A)
import { registerApplication, start } from "single-spa";
registerApplication({
name: '#prueba-organizacion/micro-prueba-app1',
app: () => System.import('#prueba-organizacion/micro-prueba-app1'),
activeWhen: (location) => location.pathname.startsWith('/app1')
});
start();
But that says: Promise\<System.Module\> is not assignable to type Application\<{}\>
Then I found ANOTHER WAY of doing the same as above:
B)
System.import('single-spa').then(({ registerApplication, start }) => {
registerApplication({
name: '#prueba-organizacion/micro-prueba-app1',
app: () => System.import('#prueba-organizacion/micro-prueba-app1'),
activeWhen: (location) => location.pathname.startsWith('/app1'),
});
start();
});
The thing here is, what's the difference between point A) and point B)?
I mean, they're importing the modules and registerApplication, start functions but one of them is throwing me an error.
I'm using "single-spa": "^5.9.3" from package.json
Thanks!
I've tried both ways, one of them is not working, the other one is working (B) as expected
I'm building a component library of just HTML snippet and corresponding js/css and I'm using Docusaurus to document those compoents. I have a document page for each component. On the document page there is an example of each component. I'd like to make the components functional (click events, keyboard nav, etc.) so I have attached the component javascript via a plugin:
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin-component-assets',
injectHtmlTags() {
return {
headTags: [
{
tagName: 'link',
attributes: {
rel: 'stylesheet',
href: 'https://example.com/css/component.min.css',
},
},
],
postBodyTags: [
{
tagName: 'script',
attributes: {
src: 'https://example.com/js/component.min.js',
},
},
],
};
},
};
};
In my docusaurus.config.js I've added my plugin:
...
plugins: [
'docusaurus-plugin-sass',
path.resolve(__dirname, 'src/plugins/docusaurus-plugin-component-assets.js')
],
...
This successfully adds the stylesheet and javascript in the correct locations. However the javascript never executes. It appears that my component javascript fires before the documentation app loads.
What am I missing? What is the correct way to add external javascript to documentation pages?
EDIT: I'm using "#docusaurus/core": "2.0.0-beta.0",
I struggled with this too (on Docusaurus v2). Eventually I understood what the Client Modules documentation was saying and did the following, which worked for me both for the initial page load and the page loaded after a navigation event. (Since this is a single-page app, it's not a full page load when you're just navigating around the documentation, and I had to handle that case separately.)
Create a new file at plugins/my-script.js (or whatever you want to call it).
Add clientModules: [require.resolve('./plugins/my-script')], to your config in docusaurus.config.js.
Insert code like this into your new file:
import ExecutionEnvironment from '#docusaurus/ExecutionEnvironment';
const doYourCustomStuff = () => {
// your JS code goes here
}
export function onRouteDidUpdate({location, previousLocation}) {
// Don't execute if we are still on the same page; the lifecycle may be fired
// because the hash changes (e.g. when navigating between headings)
if (location.pathname === previousLocation?.pathname) return;
doYourCustomStuff();
}
if (ExecutionEnvironment.canUseDOM) {
// We also need to setCodeRevealTriggers when the page first loads; otherwise,
// after reloading the page, these triggers will not be set until the user
// navigates somewhere.
window.addEventListener('load', () => {
setTimeout(doYourCustomStuff, 1000);
});
}
Then put your JS code inside the given function.
Caveat: your effects will still be broken when hot-loading changes from a yarn start dev environment. The not-too-painful workaround is to manually reload in such cases.
You can embed your components in Live Code block Docusaurus V2.
You will need to install the package using npm or yarn.
npm install --save #docusaurus/theme-live-codeblock
module.exports = {
plugins: ['#docusaurus/theme-live-codeblock'],
themeConfig: {
liveCodeBlock: {
/**
* The position of the live playground, above or under the editor
* Possible values: "top" | "bottom"
*/
playgroundPosition: 'bottom',
},
},
};
You can find detailed information about this process by using the link below.
https://docusaurus.io/docs/markdown-features/code-blocks#interactive-code-editor
It should be noted that this only works with react components.
I finished writing a small blog application using Gatsby and React.
Everything works fine when I try it locally. So I proceed with gatsby build and deploy the build folder into Netlify. However, after the deployment, the content of some of the pages is not shown despite locally everything works fine.
Description of the problem: I have a navbar with "Home", "Healthcare", "Technology", "Robotics", "Posts", "NewsLetter", and every time a user clicks on for example "Robotics" a series of posts from that category is shown.
Now locally everything works fine but as soon as I deploy I can only see "Posts" page which carries all the posts. The other choices from the navbar are not rendering the other posts categories.
Below the error I am getting from the terminal:
warn Non-deterministic routing danger: Attempting to create page: "/healthcare/", but page "/healthcare" already exists This could lead to non-deterministic routing behavior
warn Non-deterministic routing danger: Attempting to create page: "/robotics/", but page "/robotics" already exists This could lead to non-deterministic routing behavior
warn Non-deterministic routing danger: Attempting to create page: "/technology/", but page "/technology" already exists This could lead to non-deterministic routing behavior
This leads me to think that some pages are not rendered at the proper time, however, this does not explain the difference between localhost perfectly working and the deployed version not working properly.
Below my gatsby-node.js
const path = require('path')
// create pages dynamically
exports.createPages = async ({ graphql, actions }) => {
// from actions we can destructure createPage
const { createPage } = actions
// below we will be running two queries at the same time
// instead of one query only
const result = await graphql(`
{
allMdx {
nodes {
frontmatter {
slug
}
}
}
category: allMdx {
distinct(field: frontmatter___category)
}
}
`)
result.data.allMdx.nodes.forEach(({ frontmatter: { slug } }) => {
createPage({
path: `/posts/${slug}`,
component: path.resolve(`src/templates/post-template.js`),
context: {
slug,
},
})
})
result.data.category.distinct.forEach(category => {
createPage({
path: `/${category}`,
component: path.resolve(`src/templates/category-template.js`),
context: {
category,
},
})
})
}
Below is also the file post-template.js
const PostTemplate = ({data}) => {
const {
mdx: {
frontmatter: {title, category, image, date},
body,},
} = data;
return (
<Layout>
<Hero/>
<Wrapper>
{/* post info */}
<article>
<Image fluid={image.childImageSharp.fluid} />
<div className="post-info">
<span>{category}</span>
<h2>{title}</h2>
<p>{date}</p>
<div className="underline"></div>
</div>
<MDXRenderer>{body}</MDXRenderer>
</article>
{/* banner */}
<article>
<Banner/>
</article>
</Wrapper>
</Layout>
)
}
export const query = graphql`
query GetSinglePost($slug: String) {
mdx(frontmatter: {slug: {eq: $slug}}) {
frontmatter {
title
category
date(formatString: "MMMM Do, YYYY")
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
body
}
}
`
const Wrapper = styled.section`
// style goes here ...
`
export default PostTemplate
and the file category-template.js
const CategoryTemplate = props => {
console.log(props);
const {
pageContext: { category },
} = props;
const {
data: {
categories: {nodes:posts}
}
} = props;
return (
<Layout>
<Hero/>
<Posts posts={posts} title={`category / ${category}`}/>
</Layout>
)
}
export const query = graphql`
query GetCategories($category: String) {
categories: allMdx(sort: {fields: frontmatter___date, order: DESC}, filter: {frontmatter: {category: {eq: $category}}}) {
nodes {
excerpt
frontmatter {
title
author
category
date(formatString: "MMMM, Do, YYYY")
slug
readTime
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
id
}
}
}
`
export default CategoryTemplate
Below the structure of the robotics page component (all the other page components healthcare and technology have the same exact structure so I am only including one)
robotics.js
const robotics = () => {
return (
<Layout>
<SEO title="Robotics"/>
<Hero />
<h2>robotics page</h2>
</Layout>
)
}
export default robotics
In order to solve this problem I did a lot of research but before I would like to pint out that I already clean and redeploy the same application a couple of times before. So what I tried so far:
sudo gatsby clean && sudo gatsby develop after it was working I did sudo gatsby build
After deploying the build folder some pages were not rendering properly so I:
sudo m -rf node_modules sudo rm -rf public/ and sudo rm -rf package-lock.json
and procedeed with:
sudo npm install (if necessary you have to do sudo nom install --unsafe-perm) and
sudo gatsby develop && sudo gatsby build
But unfortuantely the same exact outcome: localhost everthing works fine and on Netlify some pages were not rendering at all.
So I did additional research and I studied this post which represents the same exact problem I have. Unfortunately no answer was accepted. But I tried to apply the suggestion and in fact on my gatsby-node.js I had:
exports.createPages = async ({ graphql, actions, reporter }) => {
const yourQuery= await graphql(
`
{
parameters here
}
`
if (yourQuery.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
But nothing happened.
I studied this and this too. This post says that "MDX pages cant currently be in the same directory as JSX pages" but I am not sure this is accurate and did not see it in the official documentation an example showing not to do that.
Please guide me to a potential solution on this point.
Your issue relies on the naming, and I assume that it will affect all pages (robotics, healthcare, technology, etc). This:
const robotics = () => {
return (
<Layout>
<SEO title="Robotics"/>
<Hero />
<h2>robotics page</h2>
</Layout>
)
}
export default robotics
Should be:
const Robotics = () => {
return (
<Layout>
<SEO title="Robotics"/>
<Hero />
<h2>robotics page</h2>
</Layout>
)
}
export default Robotics
Notice the capitalized component (Robotics). This is because, in React, all component names must be capitalized otherwise, React's assignation will never work.
The rest of the code, without a CodeSandbox it's difficult to check but it looks good.
In addition, something that can also lead to this behavior is the use of plugins such as gatsby-plugin-remove-trailing-slashes, so double-check that if you are using it.
Outside the scope of the question (and because of sudo gatsby develop && sudo gatsby build) it seems that you don't know the difference between gatsby develop and gatsby build, they are not complementary and it doesn't make sense to use that command.
To summarize, gatsby build creates a version of your site with production-ready optimizations like packaging up your site’s config, data, and code, and creating all the static HTML pages that eventually get rehydrated into a React application.
gatsby develop just runs a server in the background, enabling useful features and it is optimized for rapid feedback and extra debugging information. It doesn't bundle and compile your code production environment, potentially hiding some issues that will appear in Netlify, since it runs gatsby build.
With that command, you are developing your site and quitting the process by running the gatsby build, without serving it.
So, to double-check your site before pushing it to Netlify, I would suggest running a gatsby build && gatsby serve locally, since, if the Node versions in Netlify and locally are the same, the project must behave equally in both environments.
You can read the Gatsby docs for further explanations.
I did additional research and I studied this post which represents the
same exact problem I have. Unfortunately no answer was accepted.
I answered that question. The error output may be the same but it's caused but a completely different use-case. In that case, the issue relies on a wrong/unfinished promise approach, and it only affects the dynamic pages, in your case are the opposite. In your scenario, the "static" pages are the problem due to bad naming because React can't resolve the component assignation.
I have some legacy custom javascripts that I need to bundle and put them in _document.js as a link. The filename should include a hash.
What would be the best way to accomplish this?
I tried webpack configs regarding entry/output but they break NextJs build.
The problem is that we use things like window, document, etc that do crash in server side.
Ideally what is needed is to inject this into a tag, as compiled / babelified javascript code.
What I tried is
Webpack HTML Plugin plus other plugins like InlineChunk or
InlineSource plugins. They didn't work because they generate code in
an index.html that is not used by NextJS.
Using Raw Loader to get the file content. Doesn't work because it is
not babelified.
Adding a custom entry to the Webpack config, like scripts:
'path/to/my-entry.js'. Didn't work because it adds a hash name to the
file and I have no way of knowing it.
Adding a custom entry into the NextJs polyfills. I thought it made
sense, but the polyfill tag has a nomodule which prevents its code to
run on new browsers.
Another options is to add the javascript code as a string, and then using __dangerouslySetInnerHtml but the problem is that I lose linter and babel abilities there.
I tried adding it as a page, but crashes for local development and even on build
webpack.config.js
module.exports = (nextConfig = {}) =>
Object.assign({}, nextConfig, {
webpack(config, options) {
const nextJsEntries = config.entry;
config.entry = async () => {
const entries = await nextJsEntries();
entries['pages/rscripts'] = 'test/test.js';
return entries;
};
...
Then in _document.js
<script src={`${publicRuntimeConfig.ASSET_PREFIX}/_next/${this.props.buildManifest.pages['/rscripts'][2]}`} />
You can just import js file like import 'path/to/js_file' in your _app.js/app.tsx file
import "../styles/globals.css"
import "../js/test"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
This one works fine for me
I wanted to add another answer here as I came across this and I believe some things have changed in Next JS. Next now has this script component that you can use to load external scripts or dangerously set a script.
The Next.js Script component, next/script, is an extension of the HTML
element. It enables developers to set the loading priority of
third-party scripts anywhere in their application, outside next/head,
saving developer time while improving loading performance.
The cool thing is you can put them into whatever pages you want, maybe you have a script you want on a homepage, but not other pages, and Next will extract them and place them on the page based on the strategy you select. There are a few gotchas, can't load in the head, beforeInteractive is a little finicky, so I would read the link above and the actual API reference before making any choices.
import { useEffect } from 'react';
import Script from 'next/script';
function thirdPartyScript() {
useEffect(() => {
// just for fun. This actually fires
// before the onLoad callback
}, []);
return (
<Script
id="test-script"
strategy="afterInteractive"
src="/public/pages/scripts/test.js"
onLoad={() => {
console.log('Onload fires as you would expect');
}}
/>
);
}
I have a Gatsby site that queries information from a Wordpress REST API with GraphQL to dynamically create the site pages. I'd like to set my index page to be the homepage that is being created dynamically i.e home.html
I saw this post that was similar
On Gatsby CMS how can i set the about page as a index page
However, they have an about.js file that corresponds to their about page, meaning they can export it as a component and use it in index or they can even just copy the contents of that file over to index.js. The homepage that I want to set as my index is being generated dynamically and using a GraphQL query that can't be used outside of the page.js template. So I don't see an easy way to copy that over to another file.
I guess my last option would be to set my server to point to the static file in public/home.html and serve that as the site root, but the person in that posting tries to deter people from doing that.
Any ideas?
Here is page.js template that generates the pages of the site:
const PageTemplate = ({ data }) => (
<Layout>
{<h1 dangerouslySetInnerHTML={{ __html: data.currentPage.title }} />}
{
renderBlocks(gatherBlocks(data.currentPage.acf.page_blocks, data))
}
</Layout>
);
export default PageTemplate;
export const pageQuery = graphql`
query ($id: String!) {
currentPage: wordpressPage(id: {eq: $id}) {
title
id
parent {
id
}
template
acf {
page_blocks {
block_type {
acf_fc_layout
cs_title
cs_text
}
wordpress_id
}
}
}
}
`;
And here is my index page:
import React from "react"
import Layout from "../components/global/Layout"
const IndexPage = () => (
<Layout>
<h1>Hi people</h1>
<p>Welcome to the Tank Gatsby site.</p>
<p>Now go build something great.</p>
</Layout>
)
export default IndexPage
I experienced the same situation today. I used the following approach to use my dynamically created page with uri '/home'(fetched from wordpress using GraphQL query) as the home page of my Gatsby site:
Delete the default index.js file in your pages directory.
In gatsby-node.js file, change the uri
of page from '/home' to '/' just before using the CreatePage API.
Here is the sample code to achieve the desired result:
// loop through WordPress pages and create a Gatsby page for each one
pages.forEach(page => {
if(page.uri==='/home/')
page.uri = '/'
actions.createPage({
path: page.uri,
component: require.resolve(`./src/templates/${page.template.templateName}.js`),
context: {
id: page.id,
},
})
})
In the above code, pages refer to the pages fetched from WordPress using GraphQL.
I could not find an easy way to create index page programmatically. Made it work nonetheless, details below.
createRedirect is valid approach but might affect SEO and definitely affects E2E tests cause actual page content gets rendered with a small delay.
Another thing to consider is that having pages/index.js file is required in order to get index.html file generated on production build. This gets in the way of using createPage({ path: '/', ... cause in my case programmatically created index page was overwritten by the static one (made of pages/index.js). This looks like a bug to me (or rather not supported feature). Corresponding github issue.
looks like deletePage and createPage gatsby-node APIs work asynchronously, hence we have to delete index page created from static file and create the one we want in the same callback. Not 100% sure about this one, but that's my observation.
onCreatePage API is a good candidate since it gets called upon original index page creation and we can take that one out and replace it with the custom one, programmatically created.
There is a catch however - CreatePageArgs interface (unlike CreatePagesArgs) doesn't provide reference to graphql, hence fetching data might be tricky.
Final solution:
export function onCreatePage(args: CreatePageArgs): void {
const { page } = args;
if (page.path === '/') {
const { deletePage, createPage } = args.actions;
const indexPageComponentPath = path.resolve(
'./src/pages/index.tsx',
);
deletePage({
path: '/',
component: indexPageComponentPath,
});
createPage({
path: '/',
component: yourComponentPath,
});
}
}
There is a solution: use createRedirect in gatsby-node.js.
E.g.:
index.tsx
import React from 'react'
export default () => <></>
gatsby-node.js
...
exports.createPages = async ({ actions }) => {
const { createRedirect } = actions
createRedirect({
fromPath: '/',
toPath: '/home',
isPermanent: true,
redirectInBrowser: true,
})
}
...
I was able to address this by copying the contents of the page.js template into index.js , but instead of using a regular GraphQL query, which cannot be used outside of the page template, I used useStaticQuery instead and hardcoded the id of the index page I was retrieving data from.