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>
)
}
}
Related
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)
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.
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>
I'm trying to use nodejs to read the IP address and display on a html page, here is what I've done so far:
app.js
const express =require('express')
const app = express();
var os = require( 'os' );
var path = require('path')
app.get('/',function(req,res){
res.sendFile(path.join(__dirname+'/index.html'))
})
var networkInterfaces = Object.values(os.networkInterfaces())
.reduce((r,a)=>{
r = r.concat(a)
return r;
}, [])
.filter(({family, address}) => {
return family.toLowerCase().indexOf('v4') >= 0 &&
address !== '127.0.0.1'
})
.map(({address}) => address);
var ipAddresses = networkInterfaces.join(', ')
console.log(ipAddresses);
app.get('/DHCP',(req,res)=>{
return networkInterfaces[1];
});
app.listen(1000)
and the index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Page Title</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' type='text/css' media='screen' href='main.css'>
<script src='main.js'></script>
</head>
<body>
<p id="DHCP" align="middle"> DHCP:</p>
</body>
</html>
I'm new to web dev world so I just don't get how can I do it !
thanks in advance !
You need to pick a template engine (e.g. pug), then call res.render() with an html template modified according your template engine's syntax.
use something like this:
<p id="DHCP" align="middle">{{DHCP:}}</p>
This works if you previously set your application to use HTML instead of any View Engine.
I am using Next.js to develop a Server Side Rendering website and I want to make it a Progressive Web App but the problem I couldn't find the way to make it happen correctly.
When I build the application it serves correctly the service worker but there is no manifest.json and in some projects examples it serves the manifest.json but I tried it in Lighthouse audit and it says
Service worker does not successfully serve the manifest's start_url
One of the examples I used
Create Next App With Service Worker Precache
I think that the problem is that the start_url is . or / and not a valid file because in Next.js there is no index.html to serve from the start.
In summary
I am looking for an example using Next.js to build it to a dist folder and when I serve it it has a valid Service Worker and a valid Web Manifest.
A. Some file are expected to be found at "/"
You have this error because browsers expect some files to be served from the root of the server, including:
/manifest.json
/sitemap.xml
/favicon.ico
/robots.txt
/browserconfig.xml
/site.webmanifest
While the majority of these paths can be set with meta tags, older browsers just ignore them and error if these exact file names are not served.
B. Configure alternative paths and use NextJS static file
At the time of writing, there is ongoing work for supporting offline in NextJS. But it's not ready yet.
If you don't need to support older browsers and you don't want advanced SEO, you can use NextJS's Head component (see documentation) to define the manifest.json path like you would for any NextJS static file:
import Head from "next/head"
export default () => (
<Head>
<link rel="manifest" href="/static/manifest.json" />
<link rel="manifest" href="/static/site.webmanifest" />
<link rel="shortcut icon" href="/static/favicon.ico"
</Head>
)
Please note that robots.txt cannot be serve from a subdirectory (source), so this solution is not a good fit if you need to define this file.
C. Serve these files like expected
The proper solution would be to serve these files from your express server like so
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const { join } = require('path')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
const rootStaticFiles = [
'/manifest.json',
'/sitemap.xml',
'/favicon.ico',
'/robots.txt',
'/browserconfig.xml',
'/site.webmanifest',
]
if (rootStaticFiles.indexOf(parsedUrl.pathname) > -1) {
const path = join(__dirname, 'static', parsedUrl.pathname)
app.serveStatic(req, res, path)
} else {
handle(req, res, parsedUrl)
}
})
.listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
Note: This code directly comes from the NextJS examples repository
here are the steps to make your next.js progressive. check the example
npm i next-pwa
next.config.json
const withPWA = require("next-pwa");
module.exports = withPWA({
pwa: {
dest: "public",
},
...
});
add manifest.json and icons to public folder from the example. However, icons directory is missing "maskable_icon.png". So create a maskable icon from here then add this to "manifest.json".
{
"src": "path/to/maskable_icon.png",
"sizes": "196x196",
"type": "image/png",
"purpose": "any maskable"
}
add those tags to import Head from "next/head". Head is used for better SEO setting. check the documentation*
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
/>
<meta name="description" content="Description" />
<meta name="keywords" content="Keywords" />
<title>Next.js PWA Example</title>
<link rel="manifest" href="/manifest.json" />
<link
href="/icons/favicon-16x16.png"
rel="icon"
type="image/png"
sizes="16x16"
/>
<link
href="/icons/favicon-32x32.png"
rel="icon"
type="image/png"
sizes="32x32"
/>
<link rel="apple-touch-icon" href="/apple-icon.png"></link>
<meta name="theme-color" content="#317EFB" />
</Head>
lastly check if it is working. add Lighhouse extension to chrome dev tools from chrome app store and run start the performance.