Fast, Optimized & SEO-friendly

with Nuxt modules

@mayashavin

Senior Front-end Developer

Core maintainer  @StorefrontUI

Vuejs Israel Organizer

Blogger 

Ambassador

The performance audit

Is "being fast" enough for performance ?

Media Optimization

On-the-fly resize images/videos

Auto-deliver optimized format per browser

Reactive transformations on images/videos

Optimize images from non-Cloudinary urls

examples/avatar.png

Use @nuxtjs/cloudinary

/* nuxt.config.js */
export default {
  modules: [
    '@nuxtjs/cloudinary'
  ],
  cloudinary: {
    cloudName: '<your-cloud-name>'
  }
}
$cloudinary

Use @nuxtjs/cloudinary

/* nuxt.config.js */
export default {
  modules: [
    '@nuxtjs/cloudinary'
  ],
  cloudinary: {
    cloudName: '<your-cloud-name>',
    useComponent: true
  }
}
<cld-image />
<cld-video />

Use @nuxtjs/cloudinary

/* pages/avatar.vue */
export default {
 computed: {
  avatar() {
   return this.$cloudinary().url(
     `examples/avatar.png`, {
       width: 150,
       height: 100,
       radius: 'max',
       crop: 'thumb',
       quality: 'auto',
       fetchFormat: 'auto'
     })
    }
 }
}
<!-pages/avatar.vue-->
<template>
 <div>
  <img :src="avatar" alt="cat avatar">
 </div> 
</template>

Use @nuxtjs/cloudinary

Use @nuxtjs/cloudinary

<cld-image public-id="examples/avatar.png" responsive />

Styling

Optimization

Zero configuration

Minimal CSS with PurgeCSS included

Extendable and customizable CSS

Integration with other Nuxt modules

/* nuxt.config.js */
export default {
  buildModules: [
    '@nuxtjs/tailwindcss'
  ],
  tailwindcss: {
    // options
  }
}

Use @nuxtjs/tailwindcss

/* tailwind.config.js*/
module.exports = {
  theme: {},
  variants: {},
  plugins: [],
  purge: {
    enabled: process.env.NODE_ENV === 'production',
    content: [
      'components/**/*.vue',
      'layouts/**/*.vue',
      'pages/**/*.vue',
      'plugins/**/*.js',
      'nuxt.config.js'
    ]
  }
}
${prefix-for-css-property}-${color-key}-${modifier}

Use @nuxtjs/tailwindcss

Dark mode Support

nuxt/color-mode

@nuxtjs/color-mode

Setup @nuxtjs/color-mode

/* nuxt.config.js */

export default {
  buildModules: [
    '@nuxtjs/color-mode'
  ],
  //external
  colorMode: {
    // default value of $colorMode.preference
    preference: 'system',
    // fallback value if not system preference found
    fallback: 'light', 
    hid: 'nuxt-color-mode-script',
    globalName: '__NUXT_COLOR_MODE__',
    componentName: 'ColorScheme'
  }
}

Use @nuxtjs/color-mode

.${color}-mode
$colorMode
$colorMode = {
  preference, //actual selected color-mode
  value, //read-only, for detecting system color mode
  unknown //boolean - to check if need a placeholder
}

CSS Variables

/* assets/colormode.css */
:root {
  --color: #243746;
  --color-primary: #158876;
  --bg: #f3f5f4;
}

.dark-mode {
  --color: #ebf4f1;
  --color-primary: #41b38a;
  --bg: #091a28;
}

.sepia-mode {
  --color: #433422;
  --color-secondary: #504231;
  --bg: #f1e7d0;
}

body {
  background-color: var(--bg);
  color: var(--color);
  transition: background-color .3s;
}
a {
  color: var(--color-primary)
}

+ TailwindCSS

yarn add @nuxt/tailwindcss tailwindcss-dark-mode
/* ~/tailwind.config.js */
module.exports = {
 theme: {
  darkSelector: '.dark-mode',
 },
 variants: {
  backgroundColor: ['dark', 'dark-hover'],
  borderColor: ['dark', 'dark-focus'],
  textColor: ['dark', 'dark-hover', 'dark-active']
 },
 plugins: [require('tailwindcss-dark-mode')()]
}
<button
  class="btn border border-black dark:border-white"
  @click="changeMode"
>
    Button
</button>
/* nuxt.config.js */

export default {
  buildModules: [
    '@nuxtjs/tailwindcss'
  ],
}

SEO Optimization

nuxt/robots

nuxt/feed

nuxt/i18n

nuxt/sitemap

@nuxtjs/sitemap

// nuxt.config.js
export default {
 modules: [
  '@nuxtjs/sitemap'
 ],
 sitemap: {
  hostname: 'your-host-name',
  exclude: ['/secret', '/admin/**'],
  routes: async () => {
    const { $content } = require('@nuxt/content')
    const { data } = await $content('posts', { deep: true }).fetch()
    return data.map((post) => `/articles/${post.slug}`)
  }
 }
}

@nuxtjs/feed for RSS Feed

// nuxt.config.js
export default {
 modules: [
  '@nuxtjs/feed'
 ],
 feed: async () => {
   const { $content } = require('@nuxt/content')
   const posts = await $content('posts', { deep: true, text: true }).fetch()
   return [{
     path: 'feed.xml',
     create(feed) {
       feed.options = {
         title: `Feed`,
         link: 'https://example.com/feed.xml',
         description: `Feed`
       }

       posts.forEach(post => {
         feed.addItem({
           title: post.title,
           id: post.slug,
           link: `https://example.com/${post.slug}`,
           description: post.description,
           content: post.text
         })
       })
     },
     cacheTime: 1000 * 60 * 15,
     type: 'rss2'
   }]
 },
}

@nuxtjs/robots

// nuxt.config.js
export default {
  modules: [
    '@nuxtjs/robots'
  ],
  robots: {
    /* module options */
  }
}
/* /robots.txt */
User-agent: *
Disallow: '/'

nuxt-i18n

// nuxt.config.js
export default {
  modules: [
    'nuxt-i18n'
  ],
  i18n: {
    locales: ['en', 'fr'],
    defaultLocale: 'en',
    vueI18n: {
      fallbackLocale: 'en',
      messages: {
        en: require("./locales/en.json"),
        fr: require("./locales/fr.json"),
      }
    }
  }
}

Service worker registration for offline

Auto-generate manifest.json

Auto-add SEO-friendly meta data

Push notification with OneSignal

@nuxtjs/pwa

// nuxt.config.js
export default {
  modules: [
    '@nuxtjs/pwa'
  ],
  pwa: {
    icon: {},
    meta: {},
    manifest: {},
    workbox: {}
  }
}

Extra

nuxt/auth

Summary

nuxt/cloudinary for media optimization

nuxt/tailwind for CSS styling & theming

nuxt/color-mode for dark/light mode

nuxt/robots, nuxt/feed, nuxt/sitemap for SEO

nuxt/i18n for internalization

nuxt/pwa for PWA support and SEO optimization

THANK YOU

Fast, optimized & SEO friendly apps with Nuxt

By Maya Shavin

Fast, optimized & SEO friendly apps with Nuxt

  • 1,112