vue
vue copied to clipboard
Allow <noscript> in Vue templates for SSR usage
Version
2.5.17
Reproduction link
https://codepen.io/brophdawg11/pen/OBdZyX
Steps to reproduce
I'm opening this issue as a follow up to https://github.com/vuejs/vue/issues/8247, as I don't think the solution provided there is suitable for all use-cases. In a fairly simple UI, where everything is relatively positioned and flows downward, it would likely be fine to include <noscript>
outside the context of the Vue application, and it would render correctly above the entire app.
However, there are plenty of other UI's that it may not be desirable or feasible to include <noscript>
outside of the Vue application context and display it properly. The linked codepen shows a simple fixed-header layout, where including the <noscript>
tag outside the Vue application context results in the <noscript>
tag being hidden behind the fixed header, where in reality it is intended to be rendered inside the main body content, and thus below the fixed header.
The <noscript>
outside the Vue context also has the unintended effect of pushing down the main content, which has a proper margin-top to account for the static-height fixed header.
Per MDN, <noscript>
is Flow Content (https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Flow_content), it is perfectly viable to exist outside the <head>
(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript), and is perfectly valid to nest inside the DOM in a <div>
, as div's allow Flow Content as their children (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div).
Please reconsider the decision to not permit noscript
tags in Vue templates.
What is expected?
<noscript>
elements should render properly in Vue templates
What is actually happening?
<noscript>
elements do not render properly in Vue templates and cause hydration issues when in SSR
As a workaround, we are currently doing the following, but it feels quite hackish:
<template>
<main>
<div v-once v-html="noscriptHtml" />
...
</main
</template>
<script>
export default {
...
created() {
this.noscriptHtml = '<noscript>...</noscript>';
}
};
</script>
I don't know, but I think it doesn't make sense.
<noscript>
is for disabled Javascript on the browser. And, Vue apps are just Javascript. So, if Javascript is disabled, Vue templates don't works.
So, <noscript>
needs to stay out of Vue and should be in vanilla HTML.
Sorry, it's not super clear in the description here - but the linked/closed issue is specific to using SSR with Vue, as is my case. With JavaScript disabled - we still SSR a perfectly valid page.
The problem is that we include noscript
tags for when the browser doesn't have JavaScript enabled. But Vue's inability to render them client side breaks browsers that do have JavaScript enabled (insofar as hydration fails and a full re-render is performed).
Ok, even with SSR, Vue components are not place for <noscript>
. If you want to prevent errors when browser Javascript is disabled, write a global <noscript>
in the index.html
.
If you want to show an user friendly content, put a <meta>
redirect into <noscript>
and head to a static vanilla HTML page.
It makes more sense.
I disagree. What the code pen is showing is that with a fixed header UX, it is not feasible to put the noscript tag in the index.html template, completely outside of the Vue app. It must be part of the app in order to be displayed to the user in a meaningful location.
And redirecting to a separate page defeats the purpose of allowing users with JS disabled to browse and use the site. If the site is already 100% user friendly with JS disabled, why would we redirect instead of showing a message that some advanced UI functionality might not work, but core browsing and usage will be fine.
What is the reasoning behind Vue templates supporting only a subset of valid HTML markup? One of the major advantages of SSR is that is opens up the possibility of using Vue and not giving up support for JS disabled users.
Using the redirect described above would also not be very good for SEO
I don't see why <noscript>
shouldn't be allowed in vue.
Obviously, it makes no sense in the client-side but if you are using SSR to provide html & css to the client, it's an incredibly useful feature
@alexbruno It makes sense to have noscript in component in some case : like SEO (one of the main purpose of SSR provided by Vue).
If you lazy-loading image you can have a component like that :
<div class="lazy-image-component">
<img :src="inView && src" />
<noscript inline-template>
<img :src="src" />
</noscript>
</div>
In this case it's important for the img
tag contain in noscript to be at this place for better UI integration.
One small caveat, should anybody come here in search for a solution for noscript
. The children of the <noscript/>
are not rendered if it is inside a conditional.
<div>
<noscript inline-template>
<span>OMG</span>
</noscript>
</div>
sends <noscript><span>OMG</span></noscript>
to the client
<div v-if="someTrueCondition">
<noscript inline-template>
<span>OMG</span>
</noscript>
</div>
sends an empty <noscript>
tag.
What's the status on this? I would love some workaround for this that's less hacky.
@Sevensidedmarble @brophdawg11
Here is a simple workaround using Vue's <component>
component (which doesn't feel too "hacky"):
<component is="noscript">
<p>No Script Content Here</p>
</component>
This will let you get around Vue's constraints on the <noscript>
tag (also works for rendering <style>
tags as well)
You could even make it conditional for rendering on the server generated pages only (and clients/cralwers with javascript disabled will still see it):
<component v-if="$isServer" is="noscript">
<p>No Script Content Here</p>
</component>
It absolutely makes sense to include <noscript>
alternatives inside Vue components. Each component encapsualtes a defined piece of functionality, so where better to put the noscript alternative than inside that specific component. e.g.
<form action="">
...
<button @click="ajaxSubmit">Submit</button>
<noscript>
<input type="button">Submit</input>
</noscript>
</form>
Otherwise you will need to have a duplicate set of components/HTML/CSS in a different part of the codebase for users that have JS disabled.
It absolutely makes sense to include
<noscript>
alternatives inside Vue components. Each component encapsualtes a defined piece of functionality, so where better to put the noscript alternative than inside that specific component. e.g.<form action=""> ... <button @click="ajaxSubmit">Submit</button> <noscript> <input type="button">Submit</input> </noscript> </form>
Otherwise you will need to have a duplicate set of components/HTML/CSS in a different part of the codebase for users that have JS disabled.
@lukenofurther your example could be solved without the need for <noscript>
<form action="">
...
<button type="submit" @click.prevent="ajaxSubmit">Submit</button>
</form>
that said, I totally agree <noscript>
should be allowed. @tmorehouse 's solution worked well for my needs though.
@lukenofurther your example could be solved without the need for
<noscript>
<form action=""> ... <button type="submit" @click.prevent="ajaxSubmit">Submit</button> </form>
@spacedawwwg ok thanks for that, it was a simple contrived example I plucked out of nowhere though. There are real-world examples that can't be so easily solved without noscript tags.
that said, I totally agree
<noscript>
should be allowed. @tmorehouse 's solution worked well for my needs though.
I'm really glad @tmorehouse's solution works too, thank you @tmorehouse! It is still hack though, and noscript tags should be supported in the core library or at least the server renderer.
There are plenty of people on this issue alone who genuinely need it. If you want a real-world example, take a look at the markup on bbc.co.uk and you'll see loads of noscript tags that provide no-JS fallbacks deeply embedded in what look to be React components.

At the moment, the lack of this functionality out of the box is impeding Vue developers from achieving modern accessibility standards. On our project we're using noscripts to achieve WCAG level AA, which isn't even the most difficult level to achieve.
None of these are necessary.
<form @submit.prevent='ajaxSubmit'>
...
<button type='submit'>Submit</submit>
</form>
If JS is enabled, Vue capture the event. Else, native HTML action will happen.
@alexbruno that's not the point - I made that example up out of my head, it's not a real world example and it's very simple. There are other situations that can't be solved so easily with that kind of workaround.
Here is a quick component for creating a <noscript>
(as an alternative to using <component is="noscript">
).
Note that render functions allow you to use tags that vue-loader normally filters out:
export default {
name: 'NoScript',
functional: true,
render(h, { data, children }) {
return h('noscript', data, children)
}
}
And then use it like so:
<no-script>
<p>JavaScript you have not. Hmmm.</p>
</no-script>
@alexbruno - another example of where noscript is required is with an SSR and infinite scrolling with none JS pagination as a fallback (as per Google guidelines). Enabling noscript like in the following example would be a massive help as the contents of the noscript is rendered by Vue.
<infinite-loading @infinite="infiniteHandler" spinner="spiral">
...
</infinite-loading>
<noscript><Pager :info="$page.posts.pageInfo" /></noscript>
Unfortunately @tmorehouse solution doesn't work in our case.
@dozyio you could have a <div>
rendered SSR, and then when mounted set it's display to none:
<template>
<infinite-loading @infinite="infiniteHandler" spinner="spiral">
...
</infinite-loading>
<div v-show="showPager">
<Pager :info="$page.posts.pageInfo" />
</div>
</template>
<script>
export default {
data() {
return {
showPager: true
}
},
mounted() {
this.$nextTick(() => {
this.showPager = false
})
}
}
</script>
If Javascript is not enabled on the client, then the pager link will remain visible.
@tmorehouse thank you! That works - still seems like a hack around noscript though
In fact, noscript
is like an HTML native hack in modern web!
For today web apps, disabled JS is very uncommon.
@tmorehouse I tried using your solution, but once I try to nest anything inside <noscript>
on SSR phase I get
Property or method "children" is not defined on the instance but referenced during render.
I've created a code sandbox to showcase the issue https://codesandbox.io/s/functional-image-jjnn3?file=/components/NoScript.js:77-85 could you look into it? Thanks
@alexbruno <noscript>
has plenty of valid use-cases in SSR, ADA and SEO so this feature request is more than valid.
your suggestion to use a meta element and redirect to a completely different html-only page is ridiculous.
simply because you fail to envision a scenario where it would be useful doesn't make it so.
you are being a troll. please stop.
@bpossolo some people are just too active on the topics they have 0 competence in - a side effect of open dev environments :)
Ok @bpossolo and @AndrewBogdanovTSS , I think it is surprising that someone needs this markup in modern frontend applications.
But there is a lot of mentions about SSR and SEO here...
Maybe, for SSR someone would find a use for noscript
, but SEO is not the case since years ago.
Google Developer docs says that Google Search Engine is able to load, render and crawling from websites built with JS frameworks with dynamic content since 2015.
That said, I can't understand where is the SEO needs for noscript
.
So, @bpossolo and @AndrewBogdanovTSS , I'm sorry if my web dev competence is very limited to contribute, despite my 10 years of experience.
@alexbruno really, I don't see why is it so critical for you to not have it. If you don't see cases that it can be used in your daily tasks - just don't use it, it's that simple. Why you want to forbid it for other who really have a need for it is beyond my understanding
@AndrewBogdanovTSS I can't forbid anyone to use anything (maybe my children 🤣 ) !
I'm just trying to say that maybe it is not critical to have it.
Maybe there are better ways to develop Vue apps to doesn't need noscript
.
Like this:
https://developers.google.com/search/blog/2015/10/deprecating-our-ajax-crawling-scheme.html https://developers.google.com/search/blog/2014/05/understanding-web-pages-better
But, I want to apologize again if I can't be able to see your needs.
@alexbruno there are definitely valid use cases. Some sites/apps need to function for users who don't have JS enabled. That's a very tiny fraction of people, but when you're developing government services for example, these need to be usable by every person in the country (within reason), and that includes users who have JS switched off, ether by choice or JS has failed to work in their client for some reason.
For such services, it's best to take a progressive enhancement approach. This is where the noscript tag is invaluable and allows you to place fallback controls for no-JS users that aren't required for users who have JS enabled.
My point here is simple, from the point of W3C spec <no-script>
is regular tag, which is as valid to use as any other tag, it wasn't deprecated in the spec, it's there for a reason, so it should be rendered as any other valid tag, why should it throw any exceptions? What developers want to use it for - is completely other topic and shouldn't affect base functionality
Anyone know if this is fixed in Vue3? Doing some github issue house cleaning and am not working with Vue as much these days...