I am trying to set up strict CSP in my next app based on the next.js example. In my _document.js I have the following:
const cspHashOf = (text) => {
const hash = crypto.createHash('sha256')
hash.update(text)
return `'sha256-${hash.digest('base64')}'`
}
let cspStr = ''
for (let k in csp) {
cspStr += `${k} ${csp[k]}; `
}
...
render() {
const nonce = crypto.randomBytes(8).toString('base64')
let csp = {
'object-src': "'none'",
'base-uri': "'self'",
'script-src': `'nonce-${nonce}' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http: ${cspHashOf(
NextScript.getInlineScriptSource(this.props)
)}`,
}
let cspStr = ''
for (let k in csp) {
cspStr += `${k} ${csp[k]}; `
}
return (
<Html>
<Head>
<meta httpEquiv="Content-Security-Policy" content={cspStr} />
...
</Head>
<body>
...
<NextScript nonce={nonce} />
</body>
</Html>
)
In the rendered page, I have all these preloaded chunks coming from Next which are getting blocked
<link rel="preload" href="/_next/static/chunks/8be9d4c0d98df170721d8fe575c2a4bcd5b2fbe4.e7c8a9ea6074f4dcaa51.js" as="script">
<link rel="preload" href="/_next/static/chunks/863050a7585a2b3888f2e4b96c75689f1ae4a93d.a73c594ed7ed04a682dc.js" as="script">
<link rel="preload" href="/_next/static/chunks/be8560b560b3990e61cbef828a20e51dc9370d83.4acbf8ef4e1b51b0bc0f.js" as="script">
<link rel="preload" href="/_next/static/chunks/be8560b560b3990e61cbef828a20e51dc9370d83_CSS.6164c81b6ed04bb13dbd.js" as="script">
How do I prevent them from being blocked?
I was eventually able to resolve this by also passing the nonce as a prop to Head
<Head nonce={nonce}>
The documentation around this from Next.js is non-existent! I'd love if anyone finds it to share the link
Related
I want to set locale in script google map like this:
<script defer src={`https://maps.googleapis.com/maps/api/js?key=key&libraries=places&language=${locale}`}/>
but how can i get to locale in Document nextJS?
useRouter doesnot work in Document component.
export default function Document() {
return (
<Html>
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<script defer src={`https://maps.googleapis.com/maps/api/js?key=key&libraries=places&language=${locale}`}/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
You can access locale in this.props.locale.
In my project this locale uses in this case:
<Html lang={this.props.locale}>
/....
I use class component in _document.
In your variant you can access locale just in props.
import { Html, Head, Main, NextScript } from "next/document";
export default function Document(props: DocumentProps) {
return (
<Html lang={props.locale}>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
It works if you already add i18n config, and inject it to next config.
https://codesandbox.io/p/sandbox/musing-noether-7loye1?file=%2Fpages%2F_document.tsx&selection=%5B%7B%22endColumn%22%3A9%2C%22endLineNumber%22%3A11%2C%22startColumn%22%3A9%2C%22startLineNumber%22%3A11%7D%5D
I'm trying to figure out how to add a favicon file to a next.js app (with react 18).
I have made a _document page that has a head tag as follows:
import * as React from "react"
// import {createRoot} from 'react-dom/client'
import { ColorModeScript } from "#chakra-ui/react"
import Document, { Head, Html, Main, NextScript } from "next/document"
import Favicon from "../components/Favicon"
export default class AppDocument extends Document {
static getInitialProps(ctx: any) {
return Document.getInitialProps(ctx)
}
render() {
return (
<Html lang="en">
<Head>
<meta name="theme-color" key="theme-color" content="#000000" />
<meta name="description" content="name" key="description" />
<meta property="og:title" content="title goes here" key="title" />
<meta property="og:description" content="description goes here" key="og:description" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<Favicon />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
I then made a component called Favicon with:
import React from "react";
const Favicon = (): JSX.Element => {
return (
<React.Fragment>
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
</React.Fragment>
)
}
export default Favicon;
I then made a root/packages/src/public folder (this folder is at the same level and place as the pages folder that has the _document.tsx file) and saved each of the assets to it.
I don't get an error, but the favicon does not populate in the browser tab.
How can I add a favicon in nextjs?
I also tried removing the Favicon component and moving the meta tags directly to app.tsx. It still doesnt render the favicon.
I can see from the console errors that the files are not found. They are all saved in the project at public/[file name]. Public is a folder at the same level as the pages directory.
I have working a working favicon in a Next.js 12 + React 18 app by adding the icon link directly to the <Head> and the images in public/[file name].
render() {
return (
<Html lang="en">
<Head>
<link rel="shortcut icon" href="/favicon.ico" />
I assume that your React component is not working because the browser is trying to load the favicon before the page has been hydrated with any Javascript that can generate your React component favicon.
I solved this problem. You need to add the image to the public directory. Next engine will detect this favicon automatically.
but if you need to add special image or etc, you must add image file into public/static folder. and your url should be start with static/*. like this:
<React.Fragment>
<link rel="icon" sizes="76x76" href="static/apple-touch-icon.png" />
</React.Fragment>
And finally, to display the icon in the browser tab, you must perform a hard refresh Ctrl+F5;
You should just add/replace the icon file with the name favicon.ico in the root of the public folder with your icon. If you want to do SEO consider using "next-seo" package
What I do, I create a component say Meta.tsx and use it inside my Pages
import { NextSeo } from "next-seo"
import type { OpenGraph } from "next-seo/lib/types"
import { useRouter } from "next/router"
import type { MetaProps } from "../../types/content"
export const Meta = ({
type = "website",
siteName = "My-site-name",
data,
}: MetaProps): React.ReactElement => {
const router = useRouter()
// TODO Make generator based on different data
const seoData = data
const locale = "en_EN"
const baseURL = process.env.BASE_URL || window.location.origin
const currentURL = baseURL + router.asPath
const seo = {
title: seoData.title,
titleTemplate: `%s - ${siteName}`,
defaultTitle: siteName,
canonical: currentURL,
}
const openGraph: OpenGraph = {
title: seoData.title,
type,
locale,
url: currentURL,
site_name: siteName,
images: seoData.images,
}
const metaLinks = [
{
rel: "icon",
type: "image/svg+xml",
href: "/favicon.svg",
},
{
rel: "apple-touch-icon",
href: "/touch-icon-ipad.jpg",
sizes: "180x180",
},
{
rel: "mask-icon",
type: "image/svg+xml",
color: "#0d2e41",
href: "/favicon.svg",
},
{
rel: "icon",
href: "/favicon.ico",
},
]
return (
<NextSeo
{...seo}
openGraph={openGraph}
additionalLinkTags={metaLinks}
noindex={data.requireAuth || data.noIndex}
/>
)
}
I think you are just missing the correct rel attribute. This works in my project:
import {Html, Head, Main, NextScript} from 'next/document';
export default function Document() {
return (
<Html lang="en">
<Head>
<link rel="shortcut icon" href="/site/images/favicon.ico" />
...
where /site from /site/images/favicon.ico is in the public folder.
import Head from "next/head";
<Head>
// If favicon path is public/images/
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
</Head>
Then you can render this inside Header component or Lay out component. this
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" /> should work also in _document page if you pass correct `href
The easiest way is to just put the <Head> in your layout component.
import Head from "next/head"
const Layout = ({ children, home }: Props) => {
return (
<>
<div className={styles.container}>
<Head>
<link rel="icon" type="image/svg" href="/icons/YOURICON.svg" />
....rest of your <meta> tags for SEO ....
</Head>
....rest of your html/jsx like page header/main-stage/footer/etc..
)}
Make sure that your icon is in a correct format, put in a folder the image named icon.png and use imagemagick to convert it to .icon format
brew install imagemagick
convert icon.png -scale 16 tmp/16.png
convert icon.png -scale 32 tmp/32.png
convert icon.png -scale 48 tmp/48.png
convert icon.png -scale 128 tmp/128.png
convert icon.png -scale 256 tmp/256.png
convert tmp/16.png tmp/32.png tmp/48.png tmp/128.png tmp/256.png icon.ico
Then i use to generate a component for the initial head content HeadContent.js
const HeadContent = () => (
<>
<link
rel="shortcut icon"
sizes="16x16 24x24 32x32 48x48 64x64"
href="/favicon.ico"
/>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta content="yes" name="apple-mobile-web-app-capable" />
<meta
name="apple-mobile-web-app-status-bar-style"
content="black-translucent"
/>
</>
);
export default HeadContent;
Now in _document.js you can use the HeadContent.js:
import Document, { Html, Head, Main, NextScript } from "next/document";
import HeadContent from "#components/HeadContent";
export default class extends Document {
render() {
return (
<Html lang="es-co">
<Head>
<meta charSet="UTF-8" />
<HeadContent />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
inside pages/_app.tsx
import Head from "next/head";
<Head>
<link rel="icon" href="/favicon.ico" />
</Head>
I'm using Supabase CDN's for a project. When I call the createClient() function this error comes up:
createClient is not defined
I tried it with an older version of the CDN, with another browser and using the old method of SupabaseClient.createClient, but I got:
SupabaseClient is not defined
Is the CDN not working correctly or something else?
<!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">
<title>HTML</title>
<!-- Custom Styles -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<p>Nothing to see here...</p>
<!-- Project -->
<script src="https://cdn.jsdelivr.net/npm/#supabase/supabase-js#2"></script>
<script src="main.js"></script>
</body>
</html>
const SupabaseKey = // api here
const SupabaseUrl =// URL here
const options = {
db: {
schema: 'public',
},
auth: {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true
}
}
const supabase = createClient(SupabaseUrl, SupabaseKey, options)
I fixed it by adding an import statement in the JS file like this:
import createClient from 'https://cdn.jsdelivr.net/npm/#supabase/supabase-js/+esm'
The +esm at the end of the URL is from universal module and it now works.
I make site In laravel 9 with Inertiajs/vuejs 3 based on https://github.com/ColorlibHQ/AdminLTE 3 (dark mode).
I removed all jquery and use vuejs only. it works ok for for, but when I open site
is looks broken, like not all styles were loaded,
Please try enter to login into adminarea by url :
https://bi-currencies.my-demo-apps.tk/admin/currencies
credentials are in Login form
and pages looks like : https://prnt.sc/TCjBh0SefUMO 4
But if to refresh page with “CTRL+R” pages looks ok, in dark mode.
Any ideas why so and how that can be fixed?
More Details :
Adminare is based on https://github.com/ColorlibHQ/AdminLTE template(with "bootstrap": "^4.6.0").
Frontend is based on custom https://technext.github.io/space/v1.0.0/ template (with Bootstrap v5.0.1 )
I have the same design issue when I switch from admin area
frontend page https://bi-currencies.my-demo-apps.tk/home
I see this problem of other browsers of my Kubuntu 20 too.
Maybe problem is that that I use too different templates, but actually I use different layouts, so in
app/Http/Middleware/HandleInertiaRequests.php :
public function rootView(Request $request)
{
if ($request->segment(1) == 'user') {
return 'layouts/user';
}
if ($request->segment(1) == 'admin') {
return 'layouts/adminlte'; // TODO
}
return 'layouts/frontend'; // Current request is front-end
}
This project has no Redis or other chache tools installed. Sure I cleared all cache opening the site. Any other ideas?
Frontend template resources/views/layouts/frontend.blade.php :
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title inertia id="app_title">{{ config('app.name', 'Laravel') }}</title>
<link rel="shortcut icon" type="image/x-icon" href="/images/frontend_favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Yantramanav:wght#300;400;500;700;900&display=swap"
rel="stylesheet">
<!-- Styles -->
#routes
<!-- Scripts -->
<script src="//cdn.jsdelivr.net/npm/sweetalert2#11"></script>
<link rel="stylesheet" href="/css/theme.css">
<script src="/vendors/#popperjs/popper.min.js"></script>
<script src="/vendors/bootstrap/bootstrap.min.js"></script>
<script src="/vendors/is/is.min.js"></script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=window.scroll"></script>
<script src="/vendors/fontawesome/all.min.js"></script>
<script src="{{ mix('js/app.js') }}" defer></script>
</head>
<body class="bg-light sidebar-mini layout-fixed layout-footer-fixed">
#inertia
</body>
</html>
and admin template resources/views/layouts/adminlte.blade.php :
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title inertia id="app_title">{{ config('app.name', 'Laravel') }}</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
<!-- Styles -->
<link rel="stylesheet" href="/css/OverlayScrollbars.min.css">
<link rel="stylesheet" href="/css/fontawesome_all.min.css">
<link rel="stylesheet" href="/css/adminlte.min.css">
<link rel="stylesheet" href="/css/admin_custom.css">
#routes
<!-- Scripts -->
<script src="//cdn.jsdelivr.net/npm/sweetalert2#11"></script>
<script src="/js/Chart.bundle.js"></script>
<script src="/js/OverlayScrollbars.min.js"></script>
<script src="{{ mix('js/app.js') }}" defer></script>
</head>
<body class="bg-light sidebar-mini layout-fixed layout-footer-fixed">
#inertia
</body>
</html>
1 common resources/js/app.js :
require('./bootstrap');
window.Toast = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3000,
timerProgressBar: false,
didOpen: (toast) => {
toast.addEventListener('mouseenter', Swal.stopTimer)
toast.addEventListener('mouseleave', Swal.resumeTimer)
}
})
require('#fortawesome/fontawesome-free/js/all.min.js');
// Import modules...
import { createApp, h } from 'vue';
import { createInertiaApp, Link } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import mitt from 'mitt';
window.emitter = mitt();
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
import Multiselect from '#vueform/multiselect'
import VueUploadComponent from 'vue-upload-component'
import Paginate from "vuejs-paginate-next";
const app = createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.component('inertia-link', Link)
.component('Paginate', Paginate)
.component('file-upload', VueUploadComponent)
.mixin({ methods: { route } })
.component('multiselect', Multiselect)
.mount(el);
},
});
InertiaProgress.init({ color: '#4B5563' });
also in admin/settings page I added “Clear Cache” button, clicking on it next commands are run :
\Artisan::call('config:cache');
\Artisan::call('route:cache');
\Artisan::call('cache:clear');
\Artisan::call('route:cache');
\Artisan::call('route:clear');
\Artisan::call('view:clear');
\Artisan::call('clear-compiled');
but clearing cache did not help with this problem.
Can it be that reason of this problem that I have 1 common resources/js/app.js both for admin area and frontend part?
Thanks!
Once I was faced same problem with inertia and that works for me. Usually that happened when you used multiple-layouts, as well as those layout have different style-scripts.
Reason (problem):
As you have used one layout for frontend-pages (public pages) and other one for admin-pages.
So when user visited front-pages, all style scripts of frontend-layout loaded and it's work fine and looks good.
But, when user switched to admin-layout that's layout style-scripts not loaded. So, On hard-refresh (Crtl+R) that URL the appropriate layout style-scripts got loaded.
I read many article to switch layouts on run-time every article gives same solutions as you did in HandleInertiaRequests.php.
Solution:
Then I come to a point where I need to switch layout on clicking some link I did hard-loading as shown in below snippet instead of redirection by inertia-link:
<a :href="route('home')">
Home
</a>
By this way appropriate layout style-script got loaded.
My Assumption:
Moreover, I've seen your site-source-code (Ctrl+U) as shown in screenshot. You haven't loaded style-scripts by Laravel asset() helper method.
I suggest to try once by loading scripts & style link in blade file by Laravel standard approach (using asset() method) that might be a problem.
Attach CSS sheet
<link href="{{ asset('frontend/css/bootstrap.min.css') }}" rel="stylesheet">
Attach JavaScript/jQuery scripts
<script src="{{ asset('frontend/js/jquery-3.5.1.min.js') }}"></script>
After using asset() method, your links in site-source-code (Ctrl+U)
looks something like that:
Note:
make sure you must set appropriate APP_URL in .env file.
I'm attempting to switch out the css file on the fly - based on which part of the web-system the user is in (i.e. if the user is on mydomain/students/page then the page loads with students.min.css, rather than site.min.css).
I've tried doing it within the _Host.cshtml:
#page "/"
#namespace FIS2withSyncfusion.Pages
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#{
Layout = null;
//sniff the requst path and switch the stylesheet accordingly
string path = Request.Path;
string css = "site.min.css";
if (path.ToLowerInvariant().StartsWith("/students"))
{
css = "students.min.css";
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Martin's Blazor Testing Site</title>
<base href="~/" />
<link rel="stylesheet" href="css/#(css)" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
<script type="text/javascript">
function saveAsFile(filename, bytesBase64) {
if (navigator.msSaveBlob) {
//Download document in Edge browser
var data = window.atob(bytesBase64);
var bytes = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
bytes[i] = data.charCodeAt(i);
}
var blob = new Blob([bytes.buffer], { type: "application/octet-stream" });
navigator.msSaveBlob(blob, filename);
}
else {
var link = document.createElement('a');
link.download = filename;
link.href = "data:application/octet-stream;base64," + bytesBase64;
document.body.appendChild(link); // Needed for Firefox
link.click();
document.body.removeChild(link);
}
}
</script>
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<script src="_framework/blazor.server.js"></script>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
Reload
<a class="dismiss">🗙</a>
</div>
</body>
</html>
However, it doesn't seem to hit this codeblock after the first load of the site; meaning whichever page they first landed on denotes the stylesheet for the entire site.
Do I have to put this codeblock on every page or is there another way of doing this?
another way you could approach this is to create components that respond to different styling that you desire. From there you have two options:
Create dedicated css associate with the component. From the docs
Create a class toggle in the code block of the component, similar to how the NavMenu works.
After further experimentation, I've found that adding this block:
#{
//sniff the requst path and switch the stylesheet accordingly
string path = navManager.Uri;
Uri uri = new Uri(path);
List<string> parts = uri.Segments.ToList();
string module = parts[1].ToLowerInvariant().Trim('/');
string css = "site.min.css";
if (module == "students")
{
css = "students.min.css";
}
}
<head>
<link rel="stylesheet" href="css/#(css)" />
</head>
To the top of MainLayout.razor works perfectly - so long as you remove the equivalent block from _Host.cshtml