Nuxt.js: AMP example doesn't work

Created on 22 Jan 2018  Â·  10Comments  Â·  Source: nuxt/nuxt.js

Hello. I am trying to implement AMP with Nuxt.

When I run official with-amp example, it doesn't load images component. And the page loads very slow.

http://upbeat-nail.surge.sh

This question is available on Nuxt.js community (#c2314)

Most helpful comment

@iamdubx

  • first remove amp code from your app.html
  • change modifyHtml function with the following code
const modifyHtml = (html) => {
  // Add amp-custom tag to added CSS
  html = html.replace(/<style data-vue-ssr/g, '<style amp-custom data-vue-ssr')
  // Remove every script tag from generated HTML
  html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
  // Add AMP script before </head>
  const ampScript = '<script async src="https://cdn.ampproject.org/v0.js"></script>'
  html = html.replace('</head>', ampScript + '</head>')
  // Add AMP boilerplate
  const ampBoilerplate = `<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
    <noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>`;
  html = html.replace('</head>', ampBoilerplate + '</head>')
  // Make it âš¡
  html = html.replace('<html', '<html âš¡');
  return html
}
  • generate routes with prefix /amp/..
  • modify this section and check page.url is contains that route prefix (in this case /amp) like below.
'render:route': (url, page, { req, res }) => {
      if (url.startsWith('/amp/')) {
        page.html = modifyHtml(page.html)
      }
    }

And also don't forget to tell Google your amp url on your regular pages with adding necessary meta header like below

<link rel="amphtml" href="CURRENT_PAGES_AMP_VERSION">

All 10 comments

/cc @Atinux

I think you are running with SPA mode since surge.sh is static website hosting, with-amp example depends on render:route hook which is not triggering on nuxt generate command.

You can check your webpage's source code, there is no <script async src="https://cdn.ampproject.org/v0.js"></script> which should be added the hook I mentioned above.

If you want same behaviour on spa page, you should modify your nuxt.config.js file and change hooks like below.

hooks: {
    'generate:page': (page) => {
      let html = page.html
      // Add amp-custom tag to added CSS
      html = html.replace(/<style data-vue-ssr/g, '<style amp-custom data-vue-ssr')
      // Remove every script tag from generated HTML
      html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
      // Add AMP script before </head>
      const ampScript = '<script async src="https://cdn.ampproject.org/v0.js"></script>'
      html = html.replace('</head>', ampScript + '</head>')
      // Update page html
      page.html = html
    }
  }

Yes, but how can I have both AMP and non-AMP app in one place? I should create separate project for AMP version? How this config from with-amp will work along with regular Nuxt app? Am I misundestand AMP somehow maybe?

@iamdubx

  • first remove amp code from your app.html
  • change modifyHtml function with the following code
const modifyHtml = (html) => {
  // Add amp-custom tag to added CSS
  html = html.replace(/<style data-vue-ssr/g, '<style amp-custom data-vue-ssr')
  // Remove every script tag from generated HTML
  html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
  // Add AMP script before </head>
  const ampScript = '<script async src="https://cdn.ampproject.org/v0.js"></script>'
  html = html.replace('</head>', ampScript + '</head>')
  // Add AMP boilerplate
  const ampBoilerplate = `<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
    <noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>`;
  html = html.replace('</head>', ampBoilerplate + '</head>')
  // Make it âš¡
  html = html.replace('<html', '<html âš¡');
  return html
}
  • generate routes with prefix /amp/..
  • modify this section and check page.url is contains that route prefix (in this case /amp) like below.
'render:route': (url, page, { req, res }) => {
      if (url.startsWith('/amp/')) {
        page.html = modifyHtml(page.html)
      }
    }

And also don't forget to tell Google your amp url on your regular pages with adding necessary meta header like below

<link rel="amphtml" href="CURRENT_PAGES_AMP_VERSION">

How do you dynamically load the amp css and the non amp css globally?

I get this AMP error:
The parent tag of tag 'amp-sidebar' is 'main', but it can only be 'body'

How can I render a component directly in the body?

Any idea how to generate amp pages on specific route (ex. '/amp') with ssr mode (universal app) ?
The code above wont work for me.

Any idea how to generate amp pages on specific route (ex. '/amp') with ssr mode (universal app) ?
The code above wont work for me.

in your nuxt.config.js

import { modifyHtml } from './modules/configs/amp.js'
module.exports = {
...
  hooks: {
    // This hook is called before generatic static html files for SPA mode
    'generate:page': (page) => {
      page.html = modifyHtml(page.html)
    },
    // This hook is called before rendering the html to the browser
    'render:route': (url, page, {
      req,
      res
    }) => {
      if (url.startsWith('/amp/')) {
        page.html = modifyHtml(page.html)
      }
    }
  },

My ./modules/configs/amp.js
It is custom to my needs, look at // Add AMP script before to add the AMP components you want to use.

const cleanStyleTags = (html) => {
  html = html.replace(/<style data-vue-ssr/g, '<style amp-custom data-vue-ssr')
  let styles = html.match(/<style amp-custom\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi)
  html = html.replace(/<style amp-custom\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')

  let oneStyle = ''
  if (styles) {
    for (let i = 0; i < styles.length; i++) {
      styles[i] = styles[i].replace(/<style amp-custom .*>/gi, '')
      styles[i] = styles[i].replace(/<\/style>/gi, '')
      oneStyle += styles[i] + '\n'
    }
  }
  return html.replace('</head>', `\n<style amp-custom data-vue-ssr>${oneStyle}</style>\n` + '\n</head>')
}

const moveBase = (html) => {
  let base = html.match(/<base href="\/">/gi)

  if (!base) {
    return
  }

  base = base[0]
  html = html.replace(/<base href="\/">/gi, '')
  return html.replace('<head>', '<head>\n' + base + '\n\n')
}
export const modifyHtml = (html) => {
  // Remove every script tag from generated HTML
  html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')

  // Add AMP script before </head>
  const ampScript = ` <script async src="https://cdn.ampproject.org/v0.js"></script>
  <script custom-element="amp-carousel" src="https://cdn.ampproject.org/v0/amp-carousel-0.1.js" async=""></script>
  <script custom-element="amp-sidebar" src="https://cdn.ampproject.org/v0/amp-sidebar-0.1.js" async=""></script>
  <script custom-element="amp-accordion" src="https://cdn.ampproject.org/v0/amp-accordion-0.1.js" async=""></script>
  <script custom-element="amp-instagram" src="https://cdn.ampproject.org/v0/amp-instagram-0.1.js" async=""></script>
  <script custom-element="amp-social-share" src="https://cdn.ampproject.org/v0/amp-social-share-0.1.js" async=""></script>
  <script custom-element="amp-fx-collection" src="https://cdn.ampproject.org/v0/amp-fx-collection-0.1.js" async=""></script>`
  html = html.replace('</head>', ampScript + '\n</head>')

  // Add AMP boilerplate
  const ampBoilerplate = `<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>`
  html = html.replace('</head>', ampBoilerplate + '\n</head>')

  html = cleanStyleTags(html)
  html = moveBase(html)
  // Make it âš¡
  html = html.replace('<html', '<html âš¡')

  return html
}

randyhoulahan, your way 'const cleanStyleTags' is long. Because nuxt generate tags 'style' one after another. that's easier

  html = html.replace(/<style data-vue-ssr-id\W[^>]*/g, '<style amp-custom');
  html = html.replace(/<\/style><style amp-custom>/gi, '');

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaredreich picture jaredreich  Â·  3Comments

danieloprado picture danieloprado  Â·  3Comments

mikekidder picture mikekidder  Â·  3Comments

shyamchandranmec picture shyamchandranmec  Â·  3Comments

nassimbenkirane picture nassimbenkirane  Â·  3Comments