react icon indicating copy to clipboard operation
react copied to clipboard

<video /> attribute needed but not guaranteed by React

Open elrumordelaluz opened this issue 8 years ago • 76 comments

As @gaearon mentioned, React does not guarantee an attribute will be set, so probably this is not a bug.

If I understand well, react will ensure the property is set anyway.

Current behaviour React renders the html <video /> element without the attribute muted when explicitly passed.

Demo time In this pen I made a simple example setting muted to the element and obtaining the result below: pen-screen-shot

Actually the property is set well, since the original medial file has an audio track and in the pen result is muted.

The point I think is most a specific need than the expected behaviour. From the functionality POV, it is absolutely ok, my Component renders a <video /> muted as requested and so on.

But there are browsers and policies, more specifically related to this issue, Webkit and the New updated one year ago, with some interesting changes for the <video /> element. The part interested is

<video muted> elements will also be allowed to autoplay without a user gesture.

So, the specific need is to have the explicit attribute to tell the browser that this video could be autoPlayed.

There's a similar issue

elrumordelaluz avatar Aug 04 '17 20:08 elrumordelaluz

The muted property is live just like value for inputs and reflects the current value. But the muted attribute reflects the initial state of the component.

So I think the current behavior is wrong, because <video muted /> should force it to always be muted then, just like <input value="foo" /> forces it to always be "foo". So one way to resolve this is to start from scratch; introduce defaultMuted property which maps to the attribute only. Then there should be a muted property which is controlled (just like input value) and there needs to be a callback to go with that e.g. onMutedChange. However! Mute is just equal to volume being zero, so the DOM only exposes the volumechange event.

So perhaps a more DOM-truthful and minimal implementation would be to have muted be the attribute and reflect only the initial mute state.

Then React can take it further and also support a special-property volume and its corresponding event onVolumeChange. But this is a slippery slope and there are tons of such properties that should be added then (and increasingly mixing both behaviors in React is a much larger discussion). Elsewhere we recommend that people simply manually set properties via JS to control elements and I think the same general recommendation works best here too, manually set the properties and expose your fancy video player as a React component if you want to reuse it.

tl;dr IMHO yes, muted should set the attribute only, so that it only sets the initial state of the element.

syranide avatar Aug 06 '17 08:08 syranide

I am facing the same issue with Android 4.4, according to new (from a year ago), muted attribute needs to be present.

montogeek avatar Aug 07 '17 17:08 montogeek

@gaearon This should be an easy fix if you're OK with the direction (making muted set the attribute instead), although there is potential to break some existing code if someone decided to implement their own mute button and rely on this prop.

syranide avatar Aug 08 '17 07:08 syranide

Since defaultMuted is already a spec-defined property reflecting the muted attribute, it would make sense to let users utilize this API. The React API is already property-focused, so it's not a big leap. This would be the least complex change, and shouldn't be breaking.

aweary avatar Sep 15 '17 21:09 aweary

@aweary Any progress on this?

kiurchv avatar Nov 28 '17 14:11 kiurchv

@aweary Same question here, we need the muted attribute to be passed to the video tag to allow autoplaying on mobile devices. Has there been any progress in this direction?

Thank you!

NicolasPoirierB avatar Apr 04 '18 15:04 NicolasPoirierB

folks progress on an Issue is generally visible and takes the form of a PR. If you don't see a linked PR or updates on an issue assume there is no more progress.

It's unclear what the path forward is for this to me. We don't really have way of saying "sync the attribute once and then use the property" and I don't know that adding said logic is even a good idea...

jquense avatar Apr 04 '18 16:04 jquense

It's unclear what the path forward is for this to me. We don't really have way of saying "sync the attribute once and then use the property" and I don't know that adding said logic is even a good idea...

@jquense I think I understand your point, but inputs have both value and defaultValue. This is exactly the same thing, there should probably be a defaultMuted and muted. It's a "live" value and needs to be special-cased in the same way, that or otherwise just have muted prop which sets the attribute but that means users need to manually access the muted property if they want to control it precisely (just like you can with defaultValue for inputs), which is also fine.

Personally, just having muted set the attribute and leaving everything else up to the user is perfectly fine (and IMHO how input should have been implemented too).

syranide avatar Apr 04 '18 17:04 syranide

I'm hesitant to change muted to act like value; we want to get away from that special-cased behavior, since it's caused a number of problems. Of course, muted would likely be much less problematic since it's just a boolean value.

We don't really have way of saying "sync the attribute once and then use the property" and I don't know that adding said logic is even a good idea...

defaultMuted is the spec'd solution for this. It sets the initial muted value by reflecting the muted attribute, which can then be controlled using the muted property.

We don't support many attributes that aren't actually DOM attributes, but we do have defaultValue so I think there's a good argument for mirroring that API here.

aweary avatar Apr 04 '18 17:04 aweary

we want to get away from that special-cased behavior, since it's caused a number of problems.

yeah the controlling behavior ends up causing so many headaches i wouldn't want to do it again, though i agree that it's the right behavior generally.

We don't support many attributes that aren't actually DOM attributes, but we do have defaultValue so I think there's a good argument for mirroring that API here.

I like that idea, tho It's always a slippery slope, what would the SSR behavior if someone specifies defaultMuted but no muted, ? I suppose we already handle this with value and checked...

jquense avatar Apr 04 '18 17:04 jquense

I like that idea, tho It's always a slippery slope, what would the SSR behavior if someone specifies defaultMuted but no muted, ? I suppose we already handle this with value and checked...

Yeah, I imagine it would behavior just like defaultValue does for SSR, by rendering the value attribute with the provided value.

aweary avatar Apr 04 '18 17:04 aweary

ya my concern is just that most of the value/defaultValue is special cased through the codebase sort of assuming the pattern will be used just for inputs. I should probably go see how it works tho before worrying about that...

jquense avatar Apr 04 '18 17:04 jquense

My assumption is that the one use case for defaultMuted is to handle the situation where the muted attribute should be present when the video is first rendered. Since it has no dynamic behavior, per the spec, I think just defining it as a boolean attribute that must set the property (easy to do with PropertyInfoRecord) would be good enough? I hope at least 😅

aweary avatar Apr 04 '18 18:04 aweary

It could be just as easy as adding it to this list:

https://github.com/facebook/react/blob/1c2876d5b558b8591feb335d8d7204bc46f7da8a/packages/react-dom/src/shared/DOMProperty.js#L305-L322

aweary avatar Apr 04 '18 18:04 aweary

`const { Component } = React const { render } = ReactDOM

class App extends Component { render () { return ( <video style={{ width: '100%' }} src="http://clips.vorwaerts-gmbh.de/VfE_html5.mp4" autoPlay video /> ) } }

render(<App />, document.querySelector("#main"))` If you run this code there is a video playing automatically with audio. I have used video instead of muted attribute.

vinay72 avatar Oct 09 '18 21:10 vinay72

muted is an extremely important attribute for background videos. Please expose it to the browser. 🙏

In the meantime, if you need to autoplay background videos, try this:

import * as React from 'react';
import { Component } from 'react';

export class VideoComponent extends Component {
  videoContainer: HTMLDivElement;
  componentDidMount() {
    const video = document.createElement('video');
    video.autoplay = true;
    video.loop = true;
    video.muted = true; // fixes autoplay in chrome
    video.setAttribute('playsinline', 'true'); // fixes autoplay in webkit (ie. mobile safari)

    const source = document.createElement('source');
    source.src = '/path/to/your/video.mp4';
    source.type = 'video/mp4';
    video.appendChild(source);

    this.videoContainer.appendChild(video);
  }
  render() {
    return (
      <div ref={(ref) => { this.videoContainer = ref; }} />
    );
  }
}

Thank you for your consideration.

ctate avatar Dec 27 '18 23:12 ctate

Indeed, ended up doing dangerouslySetHTML because React doesn't accept muted.

tomasdev avatar Jan 24 '19 20:01 tomasdev

https://www.npmjs.com/package/react-html5video this component also seems to expose muted

Zemnmez avatar Feb 22 '19 03:02 Zemnmez

And still this issue persisted

ml242 avatar Mar 06 '19 15:03 ml242

While muted still doesn't show up as an attribute on the video element. Simply adding the playsInline makes the video play on iOS/Safari and Chrome.

<video className={s.video} src={video.url} autoPlay={video.autoplay} playsInline muted={video.mute ? 1 : null} loop={video.loop} />

As commented in 6544 this supposedly doesn't work in Chrome Android

richardnadj avatar Mar 15 '19 13:03 richardnadj

I feel like this is a source of bugs and therefore not really a feature request. Is there any plan to work on this? None of the workarounds feel nice to me.

adjohu avatar Apr 02 '19 15:04 adjohu

Agree with @adjohu, this is a bug, not a feature request. Video elements will not autoplay on in mobile Safari without the muted attribute. How hard can it be to just guarantee the attribute? I can use dangerouslySetInnerHTML for the video element, but I also need a ref to it, so that doesn't work.

keithpickering avatar Jul 17 '19 19:07 keithpickering

Almost two years and there still is no good way of doing this :/

joshverd avatar Jul 17 '19 19:07 joshverd

@joshverd is there ANY way of doing it? Haha. I can't find anything about a video element in react, with a muted attribute, and also with a ref. It's like I'm on Mars, I can't believe this is such an uncommon requirement 😬

keithpickering avatar Jul 17 '19 20:07 keithpickering

@keithpickering Actually, I got something working. It's really hack-ish but it works.

Basically, you want to save the element's 'ref' <video ref={ref => this.videoRef = ref} src="link/to/video.mp4" />

Then you have to set the element's muted property to true and play the video all from within JS.

startVideoPlayback() {
  // Muting the video
  this.videoRef.muted = true;

  // Playing the video
  this.videoRef.play();
}

This is the only way I can get Chrome to "autoplay" video, but you still have to call the 'startVideoPlayback' function. Anything else and it gets paused by Chrome's autoplay detection.

joshverd avatar Jul 17 '19 20:07 joshverd

Wow, that .play() works on mobile? I can never keep track of this dumb spec lol. That's an interesting approach, thanks!

keithpickering avatar Jul 17 '19 22:07 keithpickering

Still experiencing this issue in React 16.11.0, muted attribute not showing up on the video element even though explicitly defined. Did I miss something?

olarsson avatar Nov 07 '19 12:11 olarsson

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

stale[bot] avatar Feb 05 '20 21:02 stale[bot]

Still a valid issue

DimitarNestorov avatar Feb 05 '20 21:02 DimitarNestorov

updated a fiddle to reflect React 16 since the OP was still using 15.

still valid.

demo: https://jsfiddle.net/matt1234/vqnrc83a/1/

ml242 avatar Feb 05 '20 22:02 ml242

If you would like to have muted attribute present in HTML you can use this component (hook version) as well:

import { useRef, useEffect } from 'react'

const Player = () => {
  const videoRef = useRef(null)

  useEffect(() => {
    const { current: videoElement } = videoRef
    videoElement.setAttribute('muted', '')
  }, [])

  return (
    <video
      src="http://clips.vorwaerts-gmbh.de/VfE_html5.mp4"
      ref={videoRef}
      autoPlay
      playsInline
      muted
    />
  )
}

export default Player

Regarding autoplay: Isn't it related with that: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes

When you are using e.g. jsfiddle.net to test your code snippet, everything is rendered inside iframe there and the default behavior for e.g. Chrome is to block autoplay of such videos. If you would like the autoplay to work, you can add something like this to your local application: <iframe src="http://clips.vorwaerts-gmbh.de/VfE_html5.mp4" allow="autoplay" />

mar753 avatar Mar 29 '20 19:03 mar753

@mar753 - that's an elegant solution using hooks but it doesn't answer the question of why this prop isn't being passed, despite being a known bug for years.

ml242 avatar Mar 29 '20 20:03 ml242

Any update on this? Seems like a simple fix.

xitanggg avatar Apr 10 '20 14:04 xitanggg

@gaearon sorry to borther you, but someone should handle this problem since it's really annoying.

Losses avatar May 23 '20 08:05 Losses

People who are just trying to autoplay muted

boltcoder avatar Jun 20 '20 15:06 boltcoder

Thank you for your blog @boltcoder, but to me using dangerouslySetInnerHtml was not the solution I was expecting. I am expecting my muted attribute to be reflected inside the dom, I think browsers also expect that.

maximilientyc avatar Jun 30 '20 12:06 maximilientyc

From Google documentation about Autoplay policy changes (2017-09):

One cool way to engage users is about using muted autoplay and let them chose to unmute (see code snippet below).
Some websites already do this effectively, including Facebook, Instagram, Twitter, and YouTube.

<video id="video" muted autoplay>
<button id="unmuteButton"></button>

<script>
  unmuteButton.addEventListener('click', function() {
    video.muted = false;
  });
</script>

The example speaks by itself: chrome is expecting the dom to contain the muted attribute, just like it's expecting it to contain the autoplay attribute.

maximilientyc avatar Jun 30 '20 12:06 maximilientyc

Ofcourse it's not the right solution. But that's what I had to do to make it work in React. I'm just helping people out on tight deadlines to circumvent the limitations while react fixes (or doesn't fix the attribute to dom guarantee issue).

boltcoder avatar Jun 30 '20 12:06 boltcoder

3 years later and facing the same issue here.

<video muted is stripped out from the HTML.

Quite surprised as this feature was not considered in 3 years given that it is really necessary for who is developing a website with a lot of background/autoplay videos.

See: https://webkit.org/blog/6784/new-video-policies-for-ios/

elements will also be allowed to autoplay without a user gesture. If a element gains an audio track or becomes un-muted without a user gesture, playback will pause.

There is any specific reason to not implement this? Would a PR be welcomed?

lucashfreitas avatar Aug 17 '20 00:08 lucashfreitas

Still a bug, can't believe I was just forced to use dangerouslySetHTML to get around this…

nelsonvassalo avatar Sep 11 '20 21:09 nelsonvassalo

Still a bug, can't believe I was just forced to use dangerouslySetHTML to get around this…

you dont need to use dangerouslySetHTML, try this: https://github.com/facebook/react/issues/6544#issuecomment-705702546

cranberrymuffin avatar Oct 08 '20 17:10 cranberrymuffin

What is the latest proposal in this thread? I think the issue has stalled because there was no single proposal that people could get behind. It would help to flesh out what exactly the behavior should be with regards to whether it should be setting an attribute or a property, and whether it should be controlled or uncontrolled. Once we have a proposal, it's easier for someone to make a PR.

gaearon avatar Oct 08 '20 18:10 gaearon

proposal: the muted attribute when passed as a prop to a video component with a boolean value will be added to the dom.

ml242 avatar Oct 08 '20 18:10 ml242

What is your plan for defaultMuted vs muted, controlled vs uncontrolled, server rendering?

gaearon avatar Oct 08 '20 18:10 gaearon

incremental progress :)

ml242 avatar Oct 08 '20 18:10 ml242

Sorry, I appreciate your intent but this is not helpful and does not move the thread forward. This thread has stalled because it doesn't have a concrete proposal. If you don't have a concrete technical proposal (what you posted is vague), let's wait for somebody to post theirs. Concretely, there were concerns in https://github.com/facebook/react/issues/10389#issuecomment-378677272 that need to be addressed.

gaearon avatar Oct 08 '20 18:10 gaearon

From the React16 intro:

In the past, React used to ignore unknown DOM attributes. If you wrote JSX with an attribute that React doesn’t recognize, React would just skip it. For example, this:

// Your code: <div mycustomattribute="something" /> would render an empty div to the DOM with React 15:

// React 15 output: <div /> In React 16, we are making a change. Now, any unknown attributes will end up in the DOM:

// React 16 output: <div mycustomattribute="something" />

So does React omit 'muted' because it is known? Why does React needs to take into account each scenario including SSR / defaultMuted when we have an existing convention that developers should be able to follow?

ml242 avatar Oct 08 '20 18:10 ml242

React, in general, needs to take every scenario into account. :-) It doesn't do that for arbitrary attributes that don't have special behavior because... they don't have special behavior. In this case, there are two different backing properties, and we need make sure that the behavior makes sense (i.e. it follows React's controlled/uncontrolled model like checked and value do).

gaearon avatar Oct 08 '20 19:10 gaearon

In React, generally saying, JSX attributes specify the current value rather than the initial one.

<input value={myState} />

means that if myState gets set by some other part of the code, the input value will reflect it.

So if muted={true} meant "initially muted" it would not be consistent with how other similar JSX attributes work.

Instead, to specify the initial behavior, we use properties starting with default: defaultValue, defaultChecked. This is because DOM properties that mirror the attribute are called with this convention.

So it seems like what we really want here is support for defaultMuted?

gaearon avatar Oct 08 '20 19:10 gaearon

Works for me and helps devs conform to mdn's recomendations here.

ml242 avatar Oct 08 '20 21:10 ml242

I created a PR that fixes this issue: https://github.com/facebook/react/pull/20087

antoniopacheco avatar Oct 23 '20 22:10 antoniopacheco

note, previous PR won't add defaultMuted, instead it put into the DOM the muted property and fixes the unit testing failing with

atnpcg avatar Oct 26 '20 22:10 atnpcg

@gaearon My understanding here is that the agreed solution for this is to introduce defaultMuted to be consistent. Do you think this could be a "good first issue" to tackle by looking at other default* props or there is more than that? Is somebody already working in that direction?

pastinepolenta avatar Jan 18 '21 11:01 pastinepolenta

This issue is still under consideration?

shavidze avatar Apr 05 '21 18:04 shavidze

I'm working on audio/video autoplay and I face the problem with muted attr. https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide#autoplay_availability It would be easy to pass the policy when doing autoplay with muted=true.

st-vinguyen avatar May 13 '21 02:05 st-vinguyen

5 years now. Hopefully this will be fixed soon.

infinite0007 avatar Aug 09 '21 19:08 infinite0007

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

stale[bot] avatar Jan 09 '22 00:01 stale[bot]

bump

ljharb avatar Jan 09 '22 02:01 ljharb

Bump

jenniferabowd avatar Jan 13 '22 18:01 jenniferabowd

bump

st-vinguyen avatar Jan 14 '22 01:01 st-vinguyen

Bumps are only helpful when the stalebot comments. Other than that it’s just noise.

ljharb avatar Jan 14 '22 02:01 ljharb

Let's hope for a fix for 2030.

BeezBeez avatar Mar 30 '22 02:03 BeezBeez

Would someone like to submit a PR for the proposal in https://github.com/facebook/react/issues/10389#issuecomment-762197249? I'm not saying the team will necessarily accept it but it would help to get this rolling. I agree we need some fix for this.

gaearon avatar Mar 30 '22 02:03 gaearon

Also, the audio element has the same issue.

MustafaEminn avatar Apr 18 '22 11:04 MustafaEminn

Bump. Can get it constantly using Safari (somehow in Chrome it is happening)

MatanYemini avatar Apr 18 '22 21:04 MatanYemini

This is really ridiculous. So many years, and still no solution for mobile backdrop video?

derwaldgeist avatar Apr 19 '22 16:04 derwaldgeist

still no solution

There very much is a solution. As always, when some DOM feature is not exposed via React, you need to use a ref. For example, like in https://github.com/facebook/react/issues/10389#issuecomment-450249334 or https://github.com/facebook/react/issues/10389#issuecomment-605689475, depending on what you're trying to do.

The issue is not about that, but about a built-in solution. That's separate and does not block you from the workaround.

gaearon avatar Apr 20 '22 19:04 gaearon

I want to work on this issue . Can anyone explain me in detail what is the issue?

Himanxu1 avatar May 05 '22 10:05 Himanxu1

This works for me on chrome.

<video
...
onCanPlay={ (e) => { e.target.muted=true; e.target.play() } }
/>

ardijanr avatar May 23 '22 21:05 ardijanr

I had to do some god awful manual DOM manipulation to work around this issue that has been outstanding now for 5 years. Nearly gave me an aneurysm...

solidiquis avatar Jun 06 '22 03:06 solidiquis

It seems to me that this issue is concerned with two problems:

  1. fear of <video autoplay muted /> not auto-playing because the muted attribute isn't set
  2. add support of defaultMuted React prop

The implementation of defaultMuted wouldn't necessarily fix 1. as far as I can tell. There would still be no muted attribute if I want to control "mutedness". Unless the proposal is to always use <video defaultMuted muted /> which is confusing considering how other default* props behave.

However, I don't think there is an issue with <video autoplay muted /> in modern browsers using React 18. I created a codesandbox that tests different scenarios: https://codesandbox.io/s/video-autoplay-muted-7b65ib. The following browsers autoplay <video autoPlay muted />:

  • Chrome Version 103.0.5060.53 (Official Build) (64-bit) (Linux)
  • Chrome Version 103.0.5060.66 (Official Build) (64-bit) (Windows)
  • Chrome Version 103.0.5060.70 (Android 12; Pixel 4a)
  • Edge Version 95.0.1020.40 (Official build) (64-bit) (Windows)
  • Firefox 97.0.1 (64-Bit) (64-bit) (Windows)
  • Firefox 101.0.1 (64-bit) (Linux)

The following browsers do not autoplay this video (even if the muted attribute is present):

  • Chrome (iOS 15.5)
  • Safari (iOS 15.5)

So it would help to focus this issue if somebody could explain what concrete issue (browser + version + os) there is with not setting the muted attribute if we render <video muted />.

If there is none then we can re-focus this issue to controlled vs uncontrolled muted on <video />.

eps1lon avatar Jul 03 '22 10:07 eps1lon

This absence of muted property by default is more important due to:

  • In some browsers (e.g. Chrome 70.0) autoplay doesn't work if no muted attribute is present.
  • https://stackoverflow.com/a/17994667/2908277
  • https://stackoverflow.com/questions/49822790/html5-video-autoplay-not-working

aghArdeshir avatar Jul 14 '22 13:07 aghArdeshir

In some browsers (e.g. Chrome 70.0) autoplay doesn't work if no muted attribute is present.

Considering Chrome is an evergreen browser and version 70 is over 3 years old, it's probably not worth it to work around this quirk. Current versions are working as expected (see https://github.com/facebook/react/issues/10389#issuecomment-1173052294)

eps1lon avatar Jul 14 '22 14:07 eps1lon

@eps1lon no browser is evergreen, despite its marketing claims. Most companies' analytics still have a swath of old Chrome usage.

ljharb avatar Jul 14 '22 16:07 ljharb

@eps1lon you can't for sure consider it as "not worth it". My scenario is I have a website builder (take Ucraft, WordPress, Wix, Webflow, etc...) in which you, as a developer, don't fully control the generated dom (structure).

Website designers can drop a video widget/element on the page, and turn on/off some controls/tweaks, like AutoPlay, Mute, Loop, Controls, etc...

The website designer turns on AutoPlay (which by the way must turn on the Mute too).

To ensure your customer's website works well on all of their customers' devices, we need to have this dom property explicitly there. As I personally faced this issue in real life, in which on an iOS device autoPlay was not working.

My workaround was:

  useEffect(() => {
    // autoplay does not work on some browsers if video does not have `muted`
    // attribute:
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-autoplay

    // and reacts ignores muted attribute:
    // https://github.com/facebook/react/issues/10389

    if (videoRef.current) {
      if (mute) {
        videoRef.current.setAttribute('muted', '');
      } else {
        videoRef.current.removeAttribute('muted');
      }
    }
  }, [mute]);

aghArdeshir avatar Jul 18 '22 08:07 aghArdeshir

you can't for sure consider it as "not worth it".

Sure because that's a subjective statement. But just like we don't work around browser quirks for legacy browsers like IE 11 we might not want to work around browser quirks for unmaintained Chrome versions. Especially if the issue is fixed in later versions.

Otherwise everybody has to pay for this workaround even though they don't need to. I think it's completely fine to use your workaround if you need to support older browser versions. Arguing that usage implies the need for a fix is simply not practical considering that there are far fewer browser versions with bug fixes than browser versions without these bug fixes. So this has to be done on a case-by-case basis.

The only data point we have so far is https://github.com/facebook/react/issues/10389#issuecomment-1173052294. So if you want to contribute to this issue you might want to start collecting a browser matrix where React needs to explicitly set the muted attribute and where it doesn't. Then we can make an informed decision whether to fix the quirk in React.

eps1lon avatar Jul 18 '22 12:07 eps1lon