Would be nice to render XML sitemaps, RSS feeds, Google Shopping feeds and similar stuff using Nuxt.js.
Because at runtime we have access to Nuxt plugins such as translation, routing and API clients (data and functionality). These plugins are often tightly coupled.
When writing stuff like this in a Nuxt module we often need to replicate functionality which is already available in the Nuxt context. Additionally, it's really easy and logically to render XML using Vue templates and more in line with the rest of the application.
Easiest solution in my eyes are adding a flag property to a page component saying it should be serverSideOnly
. If this flag is set to true, it should be able to tell Nuxt that there shouldn't be rendered any layout: layout: null
.
I think there should be some minor additional changes to the webpack integration for the HTML template. Which obviously, shouldn't be included in the response.
Last but not least: serverSideOnly
page components should not be in the client bundle. (Including the routes).
File: ~/pages/xml-feeds/sitemap.xml.vue
:
<template>
<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>{{generateURL('index')}}</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
</template>
<script>
export default {
layout: null,
serverSideOnly: true,
methods: {
generateURL(routeName) {
return 'https://example.com' + this.$router.resolve(routeName).href;
}
}
}
</script>
I agree, this would be very useful to have! +1
I came here looking for the answer to a similar question: (Feel free to remove this comment in case it does not match the intention of the original issue)
Is there a way to render a page without a template? My use case would be to provide HTML fragments which could be included elsewhere (i.e. as part of a micro-frontend style application). In Nuxt-terms I need a page to be rendered without "the frame", consisting of layout and template (basically: skip rendering them as a wrapper).
@dennisreimann
Seems like a good use case for this feature.
Also I had something related in mind:
I want to render mjml templates in Vue using Nuxt.js.
This way I can leverage Nuxt.js plugins and Vue to render the template and feed that to mjml
. Also easy to do using this feature!
Looks cool, the micro-frontend concept by the way!
From a users perspective I can imagine this as the option to specify the template
for a page, just like the layout
. It'd be nice if both options could also be explicitely set to null
, skipping the wrapping of the page.
In my case I'd rather go with an additional template, because I need the ability to include the {{ HEAD }}
part (as it contains the assets needed in the fragment. Rright now I found only the "hook" to change the app.html
file for every page, but not the option to specify an individual template like fragment.html
for some pages.
Yes indeed, I agree.
However, template
is already a component property (specifying the template literal). Maybe also a bit ambigious.
What would be nice is also to be able to specify how the output is wrapped. Like:
export default {
serverSideOnly: true,
template: null,
wrapper: null
}
Specifying that the output is not wrapped at all.
Wrapper could be defaulted to the Nuxt wrapper, and overridden using a custom function:
import mjml2html from 'mjml'
export default {
serverSideOnly: true,
template: null,
wrapper: (outputString) => {
return mjml2html(outputString);
}
}
https://github.com/mjmlio/mjml
I can think of endless possibilities this way!
was also looking for this and came up with this workaround, a little hacky but does the job
on the nuxt config files, add a new hook
hooks: {
'render:route': (url, page, { req, res }) => {
if (url.match('/api/')){
let match = page.html.match(/<div id="__layout">(.+?)<\/div>/)
if (match && match.length) page.html = `<?xml version="1.0" encoding="UTF-8"?>${match[1]}`
}
}
}
on the .vue page file I have something like this
<template lang="pug">
mrss(xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:nonamespaceschemalocation='ingestion.xsd')
channel
item
title {{ catalogue_entry.title_en }}
slug {{ catalogue_entry.slug_en }}
which outputs this
<?xml version="1.0" encoding="UTF-8"?>
<mrss xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nonamespaceschemalocation="ingestion.xsd">
<channel>
<item>
<title>When I Dance</title>
<slug>when-i-dance</slug>
Now I can write XML with PUG and VUE, so happy!! :D
@dseeker great solution!
However this doesn't set the proper http headers (mainly the Content-Type
). I tried manually setting it using res.setHeader
, but it seems like nuxt overrides this somewhere before sending it.
I couldn't find any appropriate hooks to defer setting the http header to the last minute to avoid nuxt from overriding the header.
Do you have any solutions for this? Thanks!
Most helpful comment
Yes indeed, I agree.
However,
template
is already a component property (specifying the template literal). Maybe also a bit ambigious.What would be nice is also to be able to specify how the output is wrapped. Like:
Specifying that the output is not wrapped at all.
Wrapper could be defaulted to the Nuxt wrapper, and overridden using a custom function:
https://github.com/mjmlio/mjml
I can think of endless possibilities this way!