Is it possible to insert html into the <Head> section of NextJS? - html

I have a backend that returns meta tags for a specific page. I would like to insert this response into the Head section of my page component.
I can insert html into React using <div dangerouslySetInnerHTML={{__html: response.someHtml}} />.
Is it possible to do the same for the <Head> component?

You can create a BasePage component and render the Head component inside. This BasePage component will wrap up all the pages.
import HeadTags from "./Head";
const BasePage = (props) => {
const {
// you might have more props but those are related to the Heads
children,
title,
metaDescription,
} = props;
return (
<>
<HeadTags
title={title}
metaDescription={metaDescription}
canonicalPath={canonicalPath}
></HeadTags>
// you mightadd more logic here
</>
);
};
You can define HeadTags components
const HeadTags = (props) => {
const {
title = "Default tile",
metaDescription = "default metaDescriptino",
} = props;
return (
<Head>
<title>{title}</title>
{/* mobile devices by default takes the content from desktop and squueze it. But if you want it to be responsive */}
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="description" key="description" content={metaDescription} />
// you could add more tags
</Head>
);
};
Now since your page is wrapped by BasePage, you can pass the props to BasePage and it will pass it to HeadTags component so you will set meta data dynamically based on your backend returning metatags.

Related

How to inject tailwind css into an iframe without using play cdn of tailwind

I'm working with svelte and made a chat widget, for styling I used tailwindcss.
the main problem with the chat widgets are you have to render them in an iframe so that it doesn't disturb the css of its remote site. so I achieved loading it inside iframe but it rendered without tailwind styles.
// main.ts is my file where I declared the iframe
import './app.css';
import App from './App.svelte';
// you can ignore this interface if you find it confusing
interface IWidget {
iframeContainer: HTMLElement | null;
init: (props: any) => void;
createContainer: () => void;
handleMessage: (event: MessageEvent) => void;
setupListeners: () => void;
}
const app = (() => {
// new App({
// target: document.getElementById('app'),
// });
const iframeContainer = document.createElement('div');
iframeContainer.id = 'my-chat-widget';
document.body.appendChild(iframeContainer);
// create iframe
const iframe = document.createElement('iframe');
iframe.id = 'my-chat-iframe';
iframe.srcdoc = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Support</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400;500;700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="my-widget"></div>
</body>
</html>`;
document.getElementById('my-chat-widget').appendChild(iframe);
const iframeWindow: HTMLIFrameElement =
document.getElementById('my-chat-iframe');
document.getElementById('my-chat-iframe').onload = () => {
new App({
target: iframeWindow.contentWindow.document.getElementById('my-widget'),
});
};
})();
export default app;
the file above is similar to index.tsx in react
Basically I'm directly targeting my svelte app into the iframe, only the problem I'am facing is the tailwind css is not loading.
methods I tried
adding Play CDN link from tailwind into head of above file.
(this worked but not feasible as production build)
directly linking the app.css into head of above file.
(this also worked but only in dev mode because the bundler adds its content hash, I want it to be production ready)

How to set HTML lang attribute dynamically on NextJs Document?

I have a multi language site and need to set up the HTML lang attribute according the language for the each page.
I try to pass the value in context, but does not update when page changes.
Here the current code:
import Document, { Html, Head, Main, NextScript } from 'next/document'
import GlobalContext , {eLanguage }from '../components/GlobalContext' //my global context
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
static contextType = GlobalContext;
render() {
console.debug('Started')
console.debug('language:'+ this.context.language)
return (
<Html lang={eLanguage[this.context.language]}> //if the first page loaded as lang 'en' it sets 'en' and apply to all other pages.
<Head>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
Update:
The language of each page can be inferred from the page route
I believe the best solution here is to use a custom ./pages/_document.js file and override the document itself.
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
More explanation can be found here: https://nextjs.org/docs/advanced-features/custom-document
Next 10 supports Internationalized Routing and will add lang dynamically leaving you with:
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
If you use next/head you can set the language to the html tag. Anything that you pass to the Head component will be either placed in the <head> or <html>.
Next Head works similar to React Helmet, so for your case you could do something along these lines:
Create a component and import Head from "next/head"
Inside the Head tag you add the <html lang={lan} /> to the component.
Then you can pass the desired language to that component, then import the component on the desired pages.
import React from "react"
import Head from "next/head"
const Language = ({title, lang}) => (
<Head>
<html lang={lang} />
<title>{title}</title>
</Head>
)
export default Language
That html bit will be injected inside the <html> tag.
Note that even if we inject it like this the console will log the following error: TypeError: n is null.
I implemented this by adding this to next.config.js file:
i18n: {
// These are all the locales you want to support in
// your application
locales: ['en-US'],
// This is the default locale you want to be used when visiting
// a non-locale prefixed path e.g. `/hello`
defaultLocale: 'en-US' }
I didn't have the need to create a custom _document.js
Create _document.js in `pages`` folder
and use this:
import Document, { Head, Html, Main, NextScript } from 'next/document';
class MyDocument extends Document {
static async getInitialProps(context) {
const initialProps = await Document.getInitialProps(context);
return { ...initialProps };
}
render() {
return (
<Html lang={this.props.locale}>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
for localization use next.config.js
{
i18n: {
locales: ['en', 'fr', 'de',],
defaultLocale: 'en',
},
}
Nextjs versions after 10 provides default localization support,
You don't have to configure much.
It automatically adds lang attribute to html, but still there is no support until now with v12 for dir attribute to include that we can use this small trick inside _document file.
import { Head, Html, Main, NextScript } from "next/document";
function Document(props: any) {
return (
<Html dir={props.__NEXT_DATA__.locale === "en" ? "ltr" : "rtl"}>
<Head></Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
export default Document;
The end result would be
You can use a React useEffect hook to set the document's language without having to change the way that Next.js generates the HTML tag itself.
Within your page component or another appropriate component, include the useEffect hook:
import {useEffect} from "react";
And then add the hook:
const MyPage = () => {
useEffect(() => {
document.documentElement.lang = "en-us";
});
// The rest of your component
}
This passes Lighthouse's check for "hreflang", and if your site has multiple languages, you can use this to set the page language per page.
You can add the lang attribute without creating _document.js
All you need to do is add this code to your next.config.js:
i18n: {
locales: ['en'],
defaultLocale: 'en',
},
Using document object the lang attribute can be set like this:
var language= ...
switch (language) {
case en: document.documentElement.lang = 'en-us'; break;
...
}
This lang attribute will not be set on the initial html, before page is hydrated, but will still pass chrome "hreflang" audit check.
Most answers assume that the lang attribute is on the html tag is an alternative to link with a hreflang attribute. However, this is not the case!
You should provide both, as they are complementary.
This is how I implemented my hreflang links in NEXT.JS:
export const LayoutController = () => {
const router = useRouter();
return (
<Layout>
<Head>
{router.locales.map((locale) => (
<link
key={locale}
rel="alternate"
href={`${process.env.NEXT_PUBLIC_BASE_URL}${
locale === DEFAULT_LOCALE ? '' : `/${locale}`
}${router.asPath}`}
hrefLang={locale}
/>
))}
</Head>
{children}
</Layout>
);
};
These links were generated in my html page:
To overide default nextjs settings create the file ./pages/_document.js and extend the Document class as shown below:
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
Custom attributes are allowed as props, like lang:
<Html lang="en">
You can read more about custom document here: https://nextjs.org/docs/advanced-features/custom-document

ASP.NET Core - serve different HTML file for SPA?

Question
How can I serve different HTML (entry) files for an SPA application (Vue) in ASP.NET Core?
Explanation
Depending on a condition, I would like to serve a different HTML page (much like a controller would do for a non-SPA). The page would still include the entry point for Vue apps <div id="app">, but some other changes should be done before serving the HTML.
I know I somehow have to change the startup.cs file because that renders the HTML with app.UseStaticFiles() and app.UseSPAStaticFiles()
Example
Condition 1 is fulfilled, base.html is served from client -> public -> base.html
Condition 2 is fulfilled instead, special.html is served from client -> public -> special.html
Code
The basic HTML looks something like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Title</title>
</head>
<body>
<noscript>
<strong>We're sorry but this webpage doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
The important parts of startup.cs looks like this:
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// ....
app.UseStaticFiles();
app.UseSpaStaticFiles();
// ....
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
if (env.IsDevelopment())
{
endpoints.MapToVueCliProxy(
"{*path}",
new SpaOptions { SourcePath = "ClientApp" },
npmScript: "serve",
regex: "Compiled successfully");
}
// Add MapRazorPages if the app uses Razor Pages. Since Endpoint Routing includes support for many frameworks, adding Razor Pages is now opt -in.
endpoints.MapRazorPages();
});
// ....
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
});

How can I make title bar dynamic on vuetify?

I see vuetify project have title bar. But the title in public/index.html. Outside src folder
So I had trouble making it dynamic. Every page must have different title. If it's same, it's very influential on SEO
My public/index.html like this :
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>test-vuetify</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#mdi/font#latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>
<strong>We're sorry but test-vuetify doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
How can I solve this problem? I want to make title on every page is different. According to page content
Hard to tell exactly what your constraints are from the question, but in case this helps anyone else make their Vuetify title bar dynamic...
In index.js, add some "meta" to the routes:
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [
{
path: '/page1',
name: 'page-1',
component: ...,
meta: {
title: 'Page 1'
}
},
{
path: '/page2',
name: 'page-2',
component: ...,
meta: {
title: 'Page 2'
}
}
]
const router = new VueRouter({
mode: 'history',
base: (your base url),
routes: routes,
});
export default router
Then in the Vuetify title bar, use the route meta defined above:
<v-toolbar-title>{{ $route.meta.title || "Default title" }}</v-toolbar-title>

Reactjs, change the favicon in componentDidMount

I want to change the favicon of the Reacjs application according to some properties. I already have saved the icons in public folder but cannot find a way to give the path as href to a newly created element.
componentDidMount() {
const { project } = this.props.rootStore!;
let link = document.createElement('link');
const oldLink = document.getElementById('favicon');
link.id = 'favicon';
link.rel = 'shortcut icon';
link.href = // here I tried the following string literals
if (oldLink) {
document.head.removeChild(oldLink);
}
document.head.appendChild(link);
}
link.href='%PUBLIC_URL%/assets/images/' + project.id + '/favicon.ico';
// TypeError: Cannot read property 'id' of undefined
link.href = `%PUBLIC_URL%/assets/images/${project.id}/favicon.ico`;
// TypeError: Cannot read property 'id' of undefined
link.href = '%PUBLIC_URL%/assets/images/${project.id}/favicon.ico';
// GET http://localhost:3000/demo/category/showroom/%PUBLIC_URL%/assets/images/$%7Bproject.id%7D/favicon.ico 400 (Bad Request)
Now my question can be first: what is the best way to change the favicon in Reacjs (I searched a lot but did not find any answer).
second: how should I define the href.
There is no best way to do this. React offers no functionality to deal with existing DOM elements outside the application. This should be done in React as it would be done in vanilla JavaScript:
let link = document.querySelector('link[rel="shortcut icon"]') ||
document.querySelector('link[rel="icon"]');
if (!link) {
link = document.createElement('link');
link.id = 'favicon';
link.rel = 'shortcut icon';
document.head.appendChild(link);
}
link.href = `/assets/images/${id}/favicon.ico`;
href should preferably contain absolute path or URL to provide correct icon location regardless of base URL.
One React-y way to do this is using react-helmet
With that library you can change the elements inside the <head>
Eg.
import Helmet from 'react-helmet'
...
<Helmet>
<title>ABC</title>
<meta name="ABC" content: "ABC" />
<link rel="icon" type="image/png" href="favicon.ico" sizes="16x16" />
</Helmet>
npm install react-meta-tags --save
https://github.com/s-yadav/react-meta-tags
Very happy with this in my projects. Helmet is another option.
import React from 'react';
import MetaTags from 'react-meta-tags';
class Component1 extends React.Component {
render() {
return (
<div className="wrapper">
<MetaTags>
<title>Page 1</title>
<meta name="description" content="Some description." />
<meta property="og:title" content="MyApp" />
<meta property="og:image" content="path/to/image.jpg" />
</MetaTags>
<div className="content"> Some Content </div>
</div>
)
}
}