I'm trying to embed a tlk.io chat widget on my Gatsby site since no similar plugins seem to exist. I'm using the react-helmet plugin to embed that script, but nothing shows up on my page. My code you can find below.
I think it has to do with the fact that the script relies on this data-channel attribute in the div tag, but I have no idea what to do with regards to that.
import React from "react"
import Helmet from "react-helmet"
import Layout from "../components/layout"
import SEO from "../components/seo"
const LivePage = () => (
<Layout>
<SEO title="Live" />
<Helmet>
<div id="tlkio"
data-channel="hey"
style={{width:'auto',
height:400}}></div>
<script src="http://tlk.io/embed.js" type="text/javascript"></script>
</Helmet>
</Layout>
)
export default LivePage
According to Gatsby documentation about Helmet, and React Helmet <Helmet> component allows you to insert a few code that will be placed after compilation inside the <head> tag.
So, in your code, you need to remove the <div> tag and it will work like a charm.
import React from "react"
import Helmet from "react-helmet"
import Layout from "../components/layout"
import SEO from "../components/seo"
const LivePage = () => (
<Layout>
<SEO title="Live" />
<Helmet>
<script src="https://tlk.io/embed.js" type="text/javascript"/>
</Helmet>
</Layout>
)
export default LivePage
I've tested in my local machine and it works perfectly as it is shown in the following screenshot:
React Helmet is a plugin that adds its children to head tag of your webpage. You cannot add div element to head, but instead inside body of the website. Just put that div somewhere outside Helmet and you should be fine.
Related
I have a NextJS application where a third party script is only needed on certain pages. Currently it is firing on all pages, but this is causing some issues.
It's currently added after the closing body tag via a custom _document.js file. As the script has to go in the body, I can't use the head option in NextJS.
What would be the best way to only have this fire on a specific page template?
import { Html, Head, Main, NextScript } from 'next/document';
import Script from 'next/script';
export default function Document() {
return (
<Html>
<Head></Head>
<body>
<Main />
<NextScript />
{/** Below script only needed on certain pages */}
<script
src="xxx"
id="widget"
/>
</body>
</Html>
)
}
You should probably do this in _app.js instead:
import Script from 'next/script'
const allowedPages = new Set([...]);
export default function MyApp({ Component, pageProps }) {
const { asPath } = useRouter();
const enableScript = allowedPages.has(asPath);
return (
<>
<Component {...pageProps} />
{enableScript && <Script id="widget" src="xxx" />}
</>
)
}
Because you'll have access to useRouter and it will update on every page navigation. _document.js is only used on the first SSR
I try to understand how the next.js Script tag with the strategy beforeInteractive works. For testing i just used lodash. But i keep getting a ReferenceError: _ is not defined. I thought when a script is loaded with beforeInteractive it should be globally available inside my page Component since it get injected into the initial Html from the server and i could use it for example in the useEffect hook to alter a div.
Can someone explain to me why it's not working or what i'm doing wrong?
I don't installed it via npm because im trying to figure out how it works.
I have a simple _document.js and i added a Next.js script tag with the strategy beforeInteractive to this _document.js. The next.js docs says:
This strategy only works inside _document.js and is designed to load scripts that are needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side).
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<Script
src="https://unpkg.com/lodash#4.17.20"
strategy="beforeInteractive"
></Script>
</body>
</Html>
)
}
Then i have a simple page Component inside the pages folder. I added the getServerSideProps function to use ServerSideRendering.
If you export a function called getServerSideProps (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps.
import Head from 'next/head';
import {useEffect, useState} from 'react';
const TestComponent = () => {
const [change,setChange] = useState('not changed');
useEffect(()=> {
console.log(_);
setChange(_.join(['one','two'],' - '));
});
return (
<>
<Head>
<title>Test</title>
</Head>
<div>{change}</div>
</>
);
};
export async function getServerSideProps(context) {
return {
props: {},
}
}
export default TestComponent;
Update
Seems like it is indeed a bug which is fixed but not released yet
https://github.com/vercel/next.js/discussions/37098
Putting aside the fact that you should be importing Lodash as a node module, there does seem to be an issue when using next/script in _document (no matter what the external script actually is).
It turns out this is a Next.js bug that has been addressed in this PR in pre-release version v12.1.7-canary.8. To fix the issue in your project simply update Next.js to version >=12.2.0 (npm install next#latest).
As an alternative, you can use the <script> tag directly in the _document's <Head> with the defer property. This closely matches what the next/script would output.
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head>
<script
type="text/javascript"
src="https://unpkg.com/lodash#4.17.20/lodash.js"
defer
></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
First and foremost, I'm failing to see virtually any reason you'd want to do this, when you can (and should) simply use install it to node_modules. You're also going to possibly run the risk of the bundle having issues if the library type isn't a module and the next configuration requires a module.
Solution based on the question:
There's two ways.
Firstly, see the docs on this exact thing.
Please use the above method mentioned in the docs.
If that's not an option for whatever reason...
The second is a less than ideal, but working solution.
Create a folder for your static files. Ex: <root>/static/js/hello.js. Then in your _document file,
<script type="text/javascript" src="/static/hello.js"></script>
I can't include a script in helmet gatsby.
The script I want to include is this:
<script src = "assets / vendor / jquery / jquery.min.js"> </script>
I tried doing this and the console gives me an error:
import Helmet from "react-helmet";
import {withPrefix} from "gatsby";
<Helmet>
<script src = {withPrefix ('masonry.js')} />
</Helmet>
with this syntax I also tried and it gives me an error:
<Helmet>
<script src = "http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js" type = "text / javascript" />
</Helmet>
How would it be solved?
You are mixing a lot of stuff there. If your assets are internal, like the one you have in the first and second snippet, add it in your /static folder and import it like:
import { withPrefix } from "gatsby"
// ...
<Helmet>
<script src={withPrefix('jquery.min.js')} type="text/javascript" />
</Helmet>
Note the white spaces in the code, you should trail them.
If your assets are external third-party assets you can also import them in your <Helmet>, but without using withPrefix inner function. Just:
<Helmet>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js" type = "text / javascript" />
</Helmet>
Like any other component in React, your <Helmet> tag must be wrapped with something to avoid parsing issues, use it always like:
const IndexPage = () => (
<Layout>
<Helmet>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js" type="text/javascript" />
</Helmet>
</Layout>
)
I was using Bootstrap v3.3.6 and jQuery v2.2.4
Do i need to change the version not to get this error.
I am having index.html like below
<html lang="en">
<head>
<link href="css/style.css" rel="stylesheet">
</head>
<body class="nav-md">
<div id="app"></div>
<script src="./js/jquery.min.js"></script>
<script src="./js/bootstrap.min.js"></script>
<script src="./js/custom.min.js"></script>
<script src="/bundle.js"></script>
</body>
</html>
Index.js file
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import './css/bootstrap.min.css';
import './css/custom.min.css';
import './css/style.css';
import './js/jquery.min.js';
import './js/bootstrap.min.js';
import './js/custom.min.js';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import {indigoA200} from 'material-ui/styles/colors';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
const muiTheme = getMuiTheme({
palette: {
primary1Color: indigoA200,
primary2Color: indigoA200,
},
});
ReactDOM.render((
<MuiThemeProvider muiTheme={muiTheme}>
<Router history={browserHistory} routes={routes} />
</MuiThemeProvider>)
,document.getElementById('app')
);
I am unable to see the UI, I am getting Bootstrap's JavaScript requires jQuery
From the given messages your jQuery Bootstrap and other files seem to not be properly set up and contain some errors and javascript is run on a single thread and hence it is terminating execution without reaching the completion of file and throwing the error.
Please check your jQuery and Bootstrap files and try to use the CDN's from Getting Start with Bootstrap
As far as I am aware you have used imports wrong and if your index.html file has the src you are not required to import it within the index.js file and can use all the classes similarly. Please look at
How to include third party libraries in React
Working on a application which uses NextJS. Having the following problem. When I have a _document file like this:
import Document, { Head, Main, NextScript } from 'next/document';
import Header from '../components/header/header';
export default class MyDocument extends Document {
render() {
return (
<html>
<Head>
</Head>
<body>
<Header /> // Header is custom component
<Main />
<NextScript />
</body>
</html>
)
}
}
And my Header component is as follows:
import React, { Component } from 'react';
import { withRouter } from 'next/router';
import Link from 'next/link';
class navigationMobile extends Component {
render() {
return (
<nav>
<Link href="/auth"><a>Auth</a></Link>
</nav>
);
}
}
export default withRouter(navigationMobile);
Because this link is placed outside of the file it will reload the page for some reason. This will lose the application state and ruin UX and kind off defeats the purpose of implementing something with a SPA.
When I place the Header component in a page itself the routing works fine. The problem however is that I would have to put this component in every page.
Question:
Is there a way to not reload the page and still have the header component only in one place of the application?
You can use a custom _app.js. This is the correct place where to put a custom layout.
https://github.com/zeit/next.js/tree/master#custom-app