I have made a website using NUXT that needs SEO
When I use www.xml-sitemaps.com website to see if it can find all my pages, it only finds the home page, and none of the other routes. When I try other NUXT demo websites it finds them all.
My robots.txt file looks like:
User-agent: *
Disallow: /profile/
Sitemap: https://www.example.com/sitemap.xml
I am using #nuxtjs/sitemap to generate the sitemap.xml that ends up looking something like this:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url> <loc>https://www.example.com/about</loc> </url>
<url> <loc>https://www.example.com/</loc> </url>
</urlset>
And if this helps, my nuxt.config.js looks like:
module.exports = {
/*
** Headers of the page
*/
head: {
title: 'Title',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'Title' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
mode: 'spa',
loading: { color: '#3B8070' },
build: {
/*
** Run ESLint on save
*/
extend (config, { isDev, isClient }) {
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
},
css: [
'~/assets/main.css'
],
modules: [
'#nuxtjs/pwa',
[
'#nuxtjs/sitemap', {
generate: true,
hostname: 'https://www.example.com',
exclude: [
'/profile'
]
}
]
],
plugins: [
'~/plugins/uikit.js',
'~/plugins/fireauth.js'
],
manifest: {
name: 'Title',
lang: 'en'
},
router: {
middleware: 'router-auth'
},
vendor: [
'firebase',
'uikit'
]
}
I'm the creator of the nuxt sitemap module.
Your sitemap-module configuration is set in the wrong section.
Please, update your nuxt.config.js:
modules: ['#nuxtjs/pwa', '#nuxtjs/sitemap'],
sitemap: {
generate: true,
hostname: 'https://www.example.com',
exclude: [
'/profile'
]
},
plugins: [
Then run npm run generate.
Finally check your generated sitemap.xml in the \dist\ folder.
(If you have an other issue or question, you may open an issue on github project: https://github.com/nuxt-community/sitemap-module/issues)
It's important to understand what's going on with different Nuxt.js modes. Read the explanation about server side rendering in the Nuxt.js Guide, where they explain the difference between the three modes the framework can be configured to work in:
Universal (with server side rendering, so that when any page is rendered, that page will be served with all HTML rendered (SEO and crawler friendly mode)
SPA (Single Page Application) which will serve up the HTML skeleton together with css and javascript bundles, which will only be unbundled to create the initial DOM in the browser. Cool for intranet apps, bad for SEO.
Static generation of all pages (pre-rendering) so that the site can be served up in any shared hosting as simple HTML.
Once the concepts are clear, you can try changing the "mode" property in your Nuxt.js configuration file from "SPA" to "Universal", together with the other suggestion regarding xml sitemap configuration in the same nuxt.config.js file.
Additionally, you can try out and learn about different configurations by either using:
The Nuxt.js starter template discussed in the Installation Guide.
Something like Create Nuxt App that, once installed via npm install -g create-nuxt-app allows you to see how many different configurations are automatically set up for you.
Since you are in SPA mode you will not get much success with SEO, if you can run in universal mode then you will see full benefit of nuxt/vue.
See this website I did with Nuxt in universal mode.
Related
I am trying to set up a PWA for an app in Laravel (5.8) with vuejs (2.5).
This is the configuration I have in mix.js:
...
mix.js('resources/js/app.js', 'public/js')
.generateSW({
// Define runtime caching rules.
runtimeCaching: [{
// Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
// Apply a cache-first strategy.
handler: 'CacheFirst',
options: {
// Use a custom cache name.
cacheName: 'images',
// Only cache 10 images.
expiration: {
maxEntries: 10,
},
},
}],
skipWaiting: true
})
.vue()
.copy('node_modules/lodash/lodash.min.js', 'public/js')
.copy('./resources/manifest.json', 'public/dist/manifest.json')
.copy('./resources/icons', 'public/dist/')
.extract(['vue'])
.webpackConfig({
output: {
filename: '[name].js',
chunkFilename: `[name].chunk.[contenthash:8].js`,
path: path.join(__dirname, 'public/dist'),
publicPath: '/dist/'
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js',
'variables': path.resolve('resources/sass/_variables.scss')
}
},
plugins: [
new webpack.ContextReplacementPlugin(
/moment[\/\\]locale$/,
/(en|es)$/
),
]
})
.options({
processCssUrls: false,
});
...
The service worker was installed correctly and the first time it loads it caches my assets.
But the next calls I make (reload the page) don't use that cache and reload the assets from the network.
However, what I am looking for is a quick initial load after the PWA is installed and this is not happening.
I have done this before with Angular and the PWA module and the assets are loaded from cache, and if there are changes, they are updated later, which makes the initial load of the application very fast.
Can someone help me with this?
In the end I ended up using workbox-cli with this setup:
// workbox.config.js
module.exports = {
"globDirectory": "public/",
"globPatterns": [
"**/*.{js,css,ico,woff2,webmanifest}",
"**/images/icons/*",
"**/images/*",
],
// 15mb max file size
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
globIgnores: [
'**/mix-manifest.json',
'**/js/{manifest,vendor}.js',
'**/js/chunks/*',
],
"swDest": "public/service-worker.js",
"swSrc": "resources/sw-offline.js"
};
And running this at the end of my npm run prod
workbox injectManifest workbox.config.js
All credit to this repository:
https://github.com/aleksandertabor/flashcards
So first of all, I have 2 env file,
env.dev
BASE_URL=xxxx
.env
BASE_URL=xxxx
I tried to load BASE_URL from my env file, so I use nuxt/dotenv to load env file on nuxt.config.js, like this,
buildModules: [
'#nuxtjs/eslint-module',
['#nuxtjs/dotenv', { filename: '.env' + process.env.ENV }]
],
modules: [
'#nuxtjs/axios',
'#nuxtjs/auth',
'#nuxtjs/dotenv'
],
axios: {
baseURL: process.env.BASE_URL,
redirectError: {
401: '/login',
403: '/login',
404: '/notfound'
}
}
But when I try to hit API login, it's always pointing to BASE_URL on .env.
What am I do wrong?
Here I wrote a quick answer on how to user env variables: https://stackoverflow.com/a/67705541/8816585
Here is an answer on how to use a specific .env.dev file: https://github.com/nuxt-community/dotenv-module/issues/59#issuecomment-660646526
yarn add -D #nuxtjs/dotenv
In nuxt.config.js
modules: ['#nuxtjs/dotenv'],
...
buildModules: [
['#nuxtjs/dotenv',
{ filename: '.env.' + process.env.ENV }
]
],
In package.json (your probably missed this one)
"scripts": { "dev": "ENV=dev nuxt" }
And finally, do not forget to add them all to your .gitignore file.
Meanwhile, this way of working is not the recommended way as stated in the official dotenv project: https://github.com/motdotla/dotenv#should-i-have-multiple-env-files
I have a site implemented with Nuxt.js and want to share the pages on Facebook, Twitter and WhatsApp. I have to use the metatags opengraph, so I've included them. When I try to share a page into a social network, I can't see the content of the metatags. I can't see the image either, the title and description.
In my case the metatags opengraph are well suited and filled in the page. I've read how to do it in several resources, so that's not the problem. They are located in the <head> tag.
But the problem is when you use the Facebook debugger tool to see your page preview, or the https://metatags.io/ tool. When I wrote the url of my page, a 404 is returned for these tools. For the same url in a browser, you can see properly the page how it is. If you inspect the page, you can see the meta tags.
I think the problem is Twitter or Facebook are doing a GET call to my url, but the result of this call, in my understanding, is a Nuxt.js pre-render page for being executed in the browser. In the browser the JS sources are executed at the moment of the page loading, so it can inject all the metatags and many other things. I think this is how Nuxt.js SSR is working. hydratation process?
So my point is how to send the complete HTML when a GET is done, or a workaround to show my preview page in Facebook or Twitter.
Btw, I have deployed my site in Netlify, I don't know if it matters.
My nuxtjs app is working on universal mode.
Any idea to resolve the problem with the crawlers and robots?
any prerender option?
I was researching and finally I've realized what was my real problem.
The main problem is I'm deploying the Nuxt app into netlify. This platform only allows you to deploy the Nuxt app as static resources (Static Generated Deployment, Pre-rendered). I mean, there are three ways to deploy a Nuxt app: universal, spa and universal as static mode (Static Generated Deployment, Pre-rendered). The third case is a special case, in which you have to create all the static resources as html pages in deploy time. For doing that you have to use the "npm run generate" command. In my case, I have some dynamic routes and I have to do some rest queries in order to achieve the data in every case. I don't have all the db rows to generate all the html pages at the build time. It's really expensive for me. So Nuxt at the end, for the case of dynamic routes, it's generating only a html page, but including the js part which is the client to the data. So when Facebook or google is calling to the url, they are getting the html with the js, but nothing of the data, because the js is only executed in the browser, and it's there, when the page gets the data via rest. So Facebook, Twitter of WhatsApp can't get the metadata of something that is missing. Additionally these services are getting a 404 http error code when they are calling to my urls, instead of a 200 http code. So it's impossible to share the url into these social services. The solution: or generate all the html pages for every resource in the build time (case 3) or moving to another provider as firebase in order to deploy as universal app (case 1) with an express server. I think I´ll move it to firebase to achieve a good SEO and social media features.
for the other hand I've changed my head method to accomplish with the open graph metadata:
head() {
return {
title: `${MyStringHandler.truncate(defaultTitle, 65)}`,
description: defaultDescription,
link: [
{
rel: 'canonical',
href: `${routePath}`
}
],
htmlAttrs: {
lang: `${language}`
},
meta: [
{
charset: 'utf-8'
},
{
hid: 'title',
name: 'title',
content: `${MyStringHandler.truncate(defaultTitle, 65)}`
},
{
hid: 'description',
name: 'description',
content: `${MyStringHandler.truncate(overviewDefault, 155)}`
},
{
hid: 'og:type',
property: 'og:type',
content: 'website'
},
{
hid: 'og:title',
property: 'og:title',
content: `${MyStringHandler.truncate(defaultTitle, 35)}`
},
{
hid: 'og:description',
property: 'og:description',
content: `${MyStringHandler.truncate(overviewDefault, 65)}`
},
{
hid: 'og:image',
property: 'og:image',
content: URLHelper.get2XURL(path) // the size has to be more 200px at least
},
{
hid: 'og:url',
property: 'og:url',
content: `www.mydomain.com${routePath}`
},
{
hid: 'og:site_name',
property: 'og:site_name',
content: `mydomain.com`
},
{
hid: 'og:locale',
property: 'og:locale',
content: `es`
},
{
hid: 'og:image:type',
property: 'og:image:type',
content: 'image/jpeg'
},
{
hid: 'twitter:card',
property: 'twitter:card',
content: `${MyStringHandler.truncate(overviewDefault, 65)}`
},
{
hid: 'twitter:site',
property: 'twitter:site',
content: 'mydomain'
},
{
hid: 'twitter:title',
name: 'twitter:title',
content: `${MyStringHandler.truncate(defaultTitle, 35)}`
},
{
hid: 'twitter:description',
name: 'twitter:description',
content: `${MyStringHandler.truncate(overviewDefault, 65)}`
},
{
hid: 'twitter:creator',
property: 'twitter:creator',
content: 'mydomain'
},
{
hid: 'twitter:image:src',
property: 'twitter:image:src',
content: URLHelper.getImageURL(path)
},
{
hid: 'twitter:domain',
property: 'twitter:domain',
content: 'mydomain.com'
},
{
hid: 'twitter:image',
name: 'twitter:image',
content: URLHelper.getImageURL(path)
},
{
hid: 'twitter:url',
name: 'twitter:url',
content: `www.mydomain.com${routePath}`
}
]
}
}
You can test the metadata into the next pages:
https://metatags.io
https://cards-dev.twitter.com/validator
https://developers.facebook.com/tools/debug/
update:
nuxt can generate your static resources in deploy time using npm run generate, and these resources are SEO-ables. The metatags are working well.
I have my Nuxt app and I'm trying to add Vuepress on it.
I did yarn add vuepress#next -D then created the docs folder and a readme.md file in there.
The problem: The project only shows the sidebar and navbar if the .vuepress folder is outside of the docs folder; If it's inside, it won't work - Not respecting the config.js rules.
Also, it's recognising the readme.md from the Nuxt app (outside from docs folder too), not the one inside docs folder.
Can anyone help me with that?
Another question, if this above works, Am I be able to access through localhost:3000/docs instead of localhost:3000 for the Nuxt project and localhost:8080 for the docs?
That's my current folder structure (no sidebar showing - not respecting the config.js inside the .vuepress folder):
docs
|__.vuepress
| |__config.js
|
|__guides
The config.js file:
module.exports = {
title: 'Documentation',
description: 'Documentation',
themeConfig: {
sidebar: 'auto',
nav: [{
text: 'Home',
link: '/'
},
{
text: 'Guides A',
link: '/guides/apis/'
},
{
text: 'item with subitems',
items: [{
text: 'Subitem 01',
link: '/'
},
{
text: 'SubItem 02',
link: '/'
}
]
},
{
text: 'External',
link: 'https://google.com'
},
]
}
}
Vuepress version 1.0.2
Thanks.
Why you need to use both of these? If you using Nuxt you don't actually need VuePress. Check the official VuePress documentation.
Nuxt
Nuxt is capable of doing what VuePress does, but it is designed for building applications. VuePress is focused on content-centric static sites and provides features tailored for technical documentation out of the box.
I have successfully used the webfonts-loader package to generate a font and class-definitions for icons, but it isn't served by my nuxt dev server. There is a styletag in my head with:
#font-face {
font-family: "TopLoggerIcons";
src: url("/myfont.eot?#iefix") format("embedded-opentype"), url("/myfont.woff2") format("woff2");
}
But the requested http://localhost:3010/myfont.woff2 gives a 404. I had this working in the nuxt version before 2.0 (and before webpack 4), where the file is served from http://localhost:3010/_nuxt/myfont.woff2. The font is currently also served from there, but the path in the font-face declaration is wrong. I'm wondering what has changed here removing the (required?) _nuxt part in the path.
In my nuxt.config.js file I have:
build: {
extend(config, ctx) {
config.module.rules.push({
test: /plugins\/icons\.js$/,
use: ['vue-style-loader', 'css-loader', 'webfonts-loader'],
})
},
}
Now according to the example on the webfonts-loader lib I need to use the MiniCssExtractPlugin.loader instead of the vue-style-loader, but that doesn't work. I read here that it is internally used by nuxt, but i don't know how to add it here.
Hope anyone has an idea where to look...
Ok, just figured it out: you have to use the publicPath option of the webfonts-loader package:
extend(config, ctx) {
config.module.rules.push({
test: /plugins\/icons\.js$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'webfonts-loader',
options: {
publicPath: config.output.publicPath,
},
}
],
})
}
The config.output.publicPath is /_nuxt/.