Repository: facebook/react
Pull request: Put common aliases in Map/Set instead of switch over strings

This is a follow up to https://github.com/facebook/react/pull/26546

This is strictly a perf optimization since we know that switches over strings aren't optimally implemented in current engines. Basically they're a sequence of ifs.

As a result, we're better off putting the unusual cases in a Map and the very common cases in the beginning of the switch. We might be better off putting very common cases in explicit ifs - just in case the engine does optimize switches to a hash table which is potentially worse.


Changed files:
filename: packages/react-dom-bindings/src/client/DOMPropertyOperations.js
patch: @@ -140,6 +140,33 @@ export function setValueForAttribute(
   }
 }
 
+export function setValueForKnownAttribute(
+  node: Element,
+  name: string,
+  value: mixed,
+) {
+  if (value === null) {
+    node.removeAttribute(name);
+    return;
+  }
+  switch (typeof value) {
+    case 'undefined':
+    case 'function':
+    case 'symbol':
+    case 'boolean': {
+      node.removeAttribute(name);
+      return;
+    }
+  }
+  if (__DEV__) {
+    checkAttributeStringCoercion(value, name);
+  }
+  node.setAttribute(
+    name,
+    enableTrustedTypesIntegration ? (value: any) : '' + (value: any),
+  );
+}
+
 export function setValueForNamespacedAttribute(
   node: Element,
   namespace: string,

filename: packages/react-dom-bindings/src/client/ReactDOMComponent.js
patch: @@ -22,6 +22,7 @@ import {
   getValueForAttribute,
   getValueForAttributeOnCustomComponent,
   setValueForPropertyOnCustomComponent,
+  setValueForKnownAttribute,
   setValueForAttribute,
   setValueForNamespacedAttribute,
 } from './DOMPropertyOperations';
@@ -58,6 +59,7 @@ import {
 } from './CSSPropertyOperations';
 import {HTML_NAMESPACE, getIntrinsicNamespace} from './DOMNamespaces';
 import isCustomElement from '../shared/isCustomElement';
+import getAttributeAlias from '../shared/getAttributeAlias';
 import possibleStandardNames from '../shared/possibleStandardNames';
 import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
 import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
@@ -275,35 +277,6 @@ function setProp(
   props: any,
 ): void {
   switch (key) {
-    case 'style': {
-      setValueForStyles(domElement, value);
-      break;
-    }
-    case 'dangerouslySetInnerHTML': {
-      if (value != null) {
-        if (typeof value !== 'object' || !('__html' in value)) {
-          throw new Error(
-            '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
-              'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
-              'for more information.',
-          );
-        }
-        const nextHtml: any = value.__html;
-        if (nextHtml != null) {
-          if (props.children != null) {
-            throw new Error(
-              'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
-            );
-          }
-          if (disableIEWorkarounds) {
-            domElement.innerHTML = nextHtml;
-          } else {
-            setInnerHTML(domElement, nextHtml);
-          }
-        }
-      }
-      break;
-    }
     case 'children': {
       if (typeof value === 'string') {
         // Avoid setting initial textContent when the text is empty. In IE11 setting
@@ -324,50 +297,26 @@ function setProp(
       }
       break;
     }
-    case 'onScroll': {
-      if (value != null) {
-        if (__DEV__ && typeof value !== 'function') {
-          warnForInvalidEventListener(key, value);
-        }
-        listenToNonDelegatedEvent('scroll', domElement);
-      }
-      break;
-    }
-    case 'onClick': {
-      // TODO: This cast may not be sound for SVG, MathML or custom elements.
-      if (value != null) {
-        if (__DEV__ && typeof value !== 'function') {
-          warnForInvalidEventListener(key, value);
-        }
-        trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
-      }
-      break;
-    }
-    // Note: `option.selected` is not updated if `select.multiple` is
-    // disabled with `removeAttribute`. We have special logic for handling this.
-    case 'multiple': {
-      (domElement: any).multiple =
-        value && typeof value !== 'function' && typeof value !== 'symbol';
+    // These are very common props and therefore are in the beginning of the switch.
+    // TODO: aria-label is a very common prop but allows booleans so is not like the others
+    // but should ideally go in this list too.
+    case 'className':
+      setValueForKnownAttribute(domElement, 'class', value);
       break;
-    }
-    case 'muted': {
-      (domElement: any).muted =
-        value && typeof value !== 'function' && typeof value !== 'symbol';
+    case 'tabIndex':
+      // This has to be case sensitive in SVG.
+      setValueForKnownAttribute(domElement, 'tabindex', value);
       break;
-    }
-    case 'suppressContentEditableWarning':
-    case 'suppressHydrationWarning':
-    case 'defaultValue': // Reserved
-    case 'defaultChecked':
-    case 'innerHTML': {
-      // Noop
+    case 'dir':
+    case 'role':
+    case 'viewBox':
+    case 'width':
+    case 'height': {
+      setValueForKnownAttribute(domElement, key, value);
       break;
     }
-    case 'autoFocus': {
-      // We polyfill it separately on the client during commit.
-      // We could have excluded it in the property list instead of
-      // adding a special case here, but then it wouldn't be emitted
-      // on server rendering (but we *do* want to emit it in SSR).
+    case 'style': {
+      setValueForStyles(domElement, value);
       break;
     }
     // These attributes accept URLs. These must not allow javascript: URLS.
@@ -423,6 +372,77 @@ function setProp(
       domElement.setAttribute(key, sanitizedValue);
       break;
     }
+    case 'onClick': {
+      // TODO: This cast may not be sound for SVG, MathML or custom elements.
+      if (value != null) {
+        if (__DEV__ && typeof value !== 'function') {
+          warnForInvalidEventListener(key, value);
+        }
+        trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
+      }
+      break;
+    }
+    case 'onScroll': {
+      if (value != null) {
+        if (__DEV__ && typeof value !== 'function') {
+          warnForInvalidEventListener(key, value);
+        }
+        listenToNonDelegatedEvent('scroll', domElement);
+      }
+      break;
+    }
+    case 'dangerouslySetInnerHTML': {
+      if (value != null) {
+        if (typeof value !== 'object' || !('__html' in value)) {
+          throw new Error(
+            '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
+              'Please visit https://reactjs.org/link/dangerously-set-inner-html ' +
+              'for more information.',
+          );
+        }
+        const nextHtml: any = value.__html;
+        if (nextHtml != null) {
+          if (props.children != null) {
+            throw new Error(
+              'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
+            );
+          }
+          if (disableIEWorkarounds) {
+            domElement.innerHTML = nextHtml;
+          } else {
+            setInnerHTML(domElement, nextHtml);
+          }
+        }
+      }
+      break;
+    }
+    // Note: `option.selected` is not updated if `select.multiple` is
+    // disabled with `removeAttribute`. We have special logic for handling this.
+    case 'multiple': {
+      (domElement: any).multiple =
+        value && typeof value !== 'function' && typeof value !== 'symbol';
+      break;
+    }
+    case 'muted': {
+      (domElement: any).muted =
+        value && typeof value !== 'function' && typeof value !== 'symbol';
+      break;
+    }
+    case 'suppressContentEditableWarning':
+    case 'suppressHydrationWarning':
+    case 'defaultValue': // Reserved
+    case 'defaultChecked':
+    case 'innerHTML': {
+      // Noop
+      break;
+    }
+    case 'autoFocus': {
+      // We polyfill it separately on the client during commit.
+      // We could have excluded it in the property list instead of
+      // adding a special case here, but then it wouldn't be emitted
+      // on server rendering (but we *do* want to emit it in SSR).
+      break;
+    }
     case 'xlinkHref': {
       if (
         value == null ||
@@ -565,251 +585,6 @@ function setProp(
       }
       break;
     }
-    // A few React string attributes have a different name.
-    // This is a mapping from React prop names to the attribute names.
-    case 'acceptCharset':
-      setValueForAttribute(domElement, 'accept-charset', value);
-      break;
-    case 'className':
-      setValueForAttribute(domElement, 'class', value);
-      break;
-    case 'htmlFor':
-      setValueForAttribute(domElement, 'for', value);
-      break;
-    case 'httpEquiv':
-      setValueForAttribute(domElement, 'http-equiv', value);
-      break;
-    // HTML and SVG attributes, but the SVG attribute is case sensitive.
-    case 'tabIndex':
-      setValueForAttribute(domElement, 'tabindex', value);
-      break;
-    case 'crossOrigin':
-      setValueForAttribute(domElement, 'crossorigin', value);
-      break;
-    // This is a list of all SVG attributes that need special casing.
-    // Regular attributes that just accept strings.
-    case 'accentHeight':
-      setValueForAttribute(domElement, 'accent-height', value);
-      break;
-    case 'alignmentBaseline':
-      setValueForAttribute(domElement, 'alignment-baseline', value);
-      break;
-    case 'arabicForm':
-      setValueForAttribute(domElement, 'arabic-form', value);
-      break;
-    case 'baselineShift':
-      setValueForAttribute(domElement, 'baseline-shift', value);
-      break;
-    case 'capHeight':
-      setValueForAttribute(domElement, 'cap-height', value);
-      break;
-    case 'clipPath':
-      setValueForAttribute(domElement, 'clip-path', value);
-      break;
-    case 'clipRule':
-      setValueForAttribute(domElement, 'clip-rule', value);
-      break;
-    case 'colorInterpolation':
-      setValueForAttribute(domElement, 'color-interpolation', value);
-      break;
-    case 'colorInterpolationFilters':
-      setValueForAttribute(domElement, 'color-interpolation-filters', value);
-      break;
-    case 'colorProfile':
-      setValueForAttribute(domElement, 'color-profile', value);
-      break;
-    case 'colorRendering':
-      setValueForAttribute(domElement, 'color-rendering', value);
-      break;
-    case 'dominantBaseline':
-      setValueForAttribute(domElement, 'dominant-baseline', value);
-      break;
-    case 'enableBackground':
-      setValueForAttribute(domElement, 'enable-background', value);
-      break;
-    case 'fillOpacity':
-      setValueForAttribute(domElement, 'fill-opacity', value);
-      break;
-    case 'fillRule':
-      setValueForAttribute(domElement, 'fill-rule', value);
-      break;
-    case 'floodColor':
-      setValueForAttribute(domElement, 'flood-color', value);
-      break;
-    case 'floodOpacity':
-      setValueForAttribute(domElement, 'flood-opacity', value);
-      break;
-    case 'fontFamily':
-      setValueForAttribute(domElement, 'font-family', value);
-      break;
-    case 'fontSize':
-      setValueForAttribute(domElement, 'font-size', value);
-      break;
-    case 'fontSizeAdjust':
-      setValueForAttribute(domElement, 'font-size-adjust', value);
-      break;
-    case 'fontStretch':
-      setValueForAttribute(domElement, 'font-stretch', value);
-      break;
-    case 'fontStyle':
-      setValueForAttribute(domElement, 'font-style', value);
-      break;
-    case 'fontVariant':
-      setValueForAttribute(domElement, 'font-variant', value);
-      break;
-    case 'fontWeight':
-      setValueForAttribute(domElement, 'font-weight', value);
-      break;
-    case 'glyphName':
-      setValueForAttribute(domElement, 'glyph-name', value);
-      break;
-    case 'glyphOrientationHorizontal':
-      setValueForAttribute(domElement, 'glyph-orientation-horizontal', value);
-      break;
-    case 'glyphOrientationVertical':
-      setValueForAttribute(domElement, 'glyph-orientation-vertical', value);
-      break;
-    case 'horizAdvX':
-      setValueForAttribute(domElement, 'horiz-adv-x', value);
-      break;
-    case 'horizOriginX':
-      setValueForAttribute(domElement, 'horiz-origin-x', value);
-      break;
-    case 'imageRendering':
-      setValueForAttribute(domElement, 'image-rendering', value);
-      break;
-    case 'letterSpacing':
-      setValueForAttribute(domElement, 'letter-spacing', value);
-      break;
-    case 'lightingColor':
-      setValueForAttribute(domElement, 'lighting-color', value);
-      break;
-    case 'markerEnd':
-      setValueForAttribute(domElement, 'marker-end', value);
-      break;
-    case 'markerMid':
-      setValueForAttribute(domElement, 'marker-mid', value);
-      break;
-    case 'markerStart':
-      setValueForAttribute(domElement, 'marker-start', value);
-      break;
-    case 'overlinePosition':
-      setValueForAttribute(domElement, 'overline-position', value);
-      break;
-    case 'overlineThickness':
-      setValueForAttribute(domElement, 'overline-thickness', value);
-      break;
-    case 'paintOrder':
-      setValueForAttribute(domElement, 'paint-order', value);
-      break;
-    case 'panose-1':
-      setValueForAttribute(domElement, 'panose-1', value);
-      break;
-    case 'pointerEvents':
-      setValueForAttribute(domElement, 'pointer-events', value);
-      break;
-    case 'renderingIntent':
-      setValueForAttribute(domElement, 'rendering-intent', value);
-      break;
-    case 'shapeRendering':
-      setValueForAttribute(domElement, 'shape-rendering', value);
-      break;
-    case 'stopColor':
-      setValueForAttribute(domElement, 'stop-color', value);
-      break;
-    case 'stopOpacity':
-      setValueForAttribute(domElement, 'stop-opacity', value);
-      break;
-    case 'strikethroughPosition':
-      setValueForAttribute(domElement, 'strikethrough-position', value);
-      break;
-    case 'strikethroughThickness':
-      setValueForAttribute(domElement, 'strikethrough-thickness', value);
-      break;
-    case 'strokeDasharray':
-      setValueForAttribute(domElement, 'stroke-dasharray', value);
-      break;
-    case 'strokeDashoffset':
-      setValueForAttribute(domElement, 'stroke-dashoffset', value);
-      break;
-    case 'strokeLinecap':
-      setValueForAttribute(domElement, 'stroke-linecap', value);
-      break;
-    case 'strokeLinejoin':
-      setValueForAttribute(domElement, 'stroke-linejoin', value);
-      break;
-    case 'strokeMiterlimit':
-      setValueForAttribute(domElement, 'stroke-miterlimit', value);
-      break;
-    case 'strokeOpacity':
-      setValueForAttribute(domElement, 'stroke-opacity', value);
-      break;
-    case 'strokeWidth':
-      setValueForAttribute(domElement, 'stroke-width', value);
-      break;
-    case 'textAnchor':
-      setValueForAttribute(domElement, 'text-anchor', value);
-      break;
-    case 'textDecoration':
-      setValueForAttribute(domElement, 'text-decoration', value);
-      break;
-    case 'textRendering':
-      setValueForAttribute(domElement, 'text-rendering', value);
-      break;
-    case 'transformOrigin':
-      setValueForAttribute(domElement, 'transform-origin', value);
-      break;
-    case 'underlinePosition':
-      setValueForAttribute(domElement, 'underline-position', value);
-      break;
-    case 'underlineThickness':
-      setValueForAttribute(domElement, 'underline-thickness', value);
-      break;
-    case 'unicodeBidi':
-      setValueForAttribute(domElement, 'unicode-bidi', value);
-      break;
-    case 'unicodeRange':
-      setValueForAttribute(domElement, 'unicode-range', value);
-      break;
-    case 'unitsPerEm':
-      setValueForAttribute(domElement, 'units-per-em', value);
-      break;
-    case 'vAlphabetic':
-      setValueForAttribute(domElement, 'v-alphabetic', value);
-      break;
-    case 'vHanging':
-      setValueForAttribute(domElement, 'v-hanging', value);
-      break;
-    case 'vIdeographic':
-      setValueForAttribute(domElement, 'v-ideographic', value);
-      break;
-    case 'vMathematical':
-      setValueForAttribute(domElement, 'v-mathematical', value);
-      break;
-    case 'vectorEffect':
-      setValueForAttribute(domElement, 'vector-effect', value);
-      break;
-    case 'vertAdvY':
-      setValueForAttribute(domElement, 'vert-adv-y', value);
-      break;
-    case 'vertOriginX':
-      setValueForAttribute(domElement, 'vert-origin-x', value);
-      break;
-    case 'vertOriginY':
-      setValueForAttribute(domElement, 'vert-origin-y', value);
-      break;
-    case 'wordSpacing':
-      setValueForAttribute(domElement, 'word-spacing', value);
-      break;
-    case 'writingMode':
-      setValueForAttribute(domElement, 'writing-mode', value);
-      break;
-    case 'xmlnsXlink':
-      setValueForAttribute(domElement, 'xmlns:xlink', value);
-      break;
-    case 'xHeight':
-      setValueForAttribute(domElement, 'x-height', value);
-      break;
     case 'xlinkActuate':
       setValueForNamespacedAttribute(
         domElement,
@@ -904,7 +679,8 @@ function setProp(
           warnForInvalidEventListener(key, value);
         }
       } else {
-        setValueForAttribute(domElement, key, value);
+        const attributeName = getAttributeAlias(key);
+        setValueForAttribute(domElement, attributeName, value);
       }
     }
   }
@@ -1018,6 +794,17 @@ export function setInitialProperties(
   // TODO: Make sure that we check isMounted before firing any of these events.
 
   switch (tag) {
+    case 'div':
+    case 'span':
+    case 'svg':
+    case 'path':
+    case 'a':
+    case 'g':
+    case 'p':
+    case 'li': {
+      // Fast track the most common tag types
+      break;
+    }
     case 'input': {
       ReactDOMInputInitWrapperState(domElement, props);
       // We listen to this event in case to ensure emulated bubble
@@ -1032,6 +819,21 @@ export function setInitialProperties(
           continue;
         }
         switch (propKey) {
+          case 'type': {
+            // Fast path since 'type' is very common on inputs
+            if (
+              propValue != null &&
+              typeof propValue !== 'function' &&
+              typeof propValue !== 'symbol' &&
+              typeof propValue !== 'boolean'
+            ) {
+              if (__DEV__) {
+                checkAttributeStringCoercion(propValue, propKey);
+              }
+              domElement.setAttribute(propKey, propValue);
+            }
+            break;
+          }
           case 'checked': {
             const node = ((domElement: any): InputWithWrapperState);
             const checked =
@@ -1246,30 +1048,32 @@ export function setInitialProperties(
       }
       return;
     }
+    default: {
+      if (isCustomElement(tag, props)) {
+        for (const propKey in props) {
+          if (!props.hasOwnProperty(propKey)) {
+            continue;
+          }
+          const propValue = props[propKey];
+          if (propValue == null) {
+            continue;
+          }
+          setPropOnCustomElement(domElement, tag, propKey, propValue, props);
+        }
+        return;
+      }
+    }
   }
 
-  if (isCustomElement(tag, props)) {
-    for (const propKey in props) {
-      if (!props.hasOwnProperty(propKey)) {
-        continue;
-      }
-      const propValue = props[propKey];
-      if (propValue == null) {
-        continue;
-      }
-      setPropOnCustomElement(domElement, tag, propKey, propValue, props);
+  for (const propKey in props) {
+    if (!props.hasOwnProperty(propKey)) {
+      continue;
     }
-  } else {
-    for (const propKey in props) {
-      if (!props.hasOwnProperty(propKey)) {
-        continue;
-      }
-      const propValue = props[propKey];
-      if (propValue == null) {
-        continue;
-      }
-      setProp(domElement, tag, propKey, propValue, props);
+    const propValue = props[propKey];
+    if (propValue == null) {
+      continue;
     }
+    setProp(domElement, tag, propKey, propValue, props);
   }
 }
 
@@ -1395,7 +1199,18 @@ export function updateProperties(
   nextProps: Object,
 ): void {
   switch (tag) {
-    case 'input': {
+    case 'div':
+    case 'span':
+    case 'svg':
+    case 'path':
+    case 'a':
+    case 'g':
+    case 'p':
+    case 'li': {
+      // Fast track the most common tag types
+      break;
+    }
+    case 'input': {
       // Update checked *before* name.
       // In the middle of an update, it is possible to have multiple checked.
       // When a checked radio tries to change name, browser makes another radio's checked false.
@@ -1552,21 +1367,29 @@ export function updateProperties(
       }
       return;
     }
+    default: {
+      if (isCustomElement(tag, nextProps)) {
+        for (let i = 0; i < updatePayload.length; i += 2) {
+          const propKey = updatePayload[i];
+          const propValue = updatePayload[i + 1];
+          setPropOnCustomElement(
+            domElement,
+            tag,
+            propKey,
+            propValue,
+            nextProps,
+          );
+        }
+        return;
+      }
+    }
   }
 
   // Apply the diff.
-  if (isCustomElement(tag, nextProps)) {
-    for (let i = 0; i < updatePayload.length; i += 2) {
-      const propKey = updatePayload[i];
-      const propValue = updatePayload[i + 1];
-      setPropOnCustomElement(domElement, tag, propKey, propValue, nextProps);
-    }
-  } else {
-    for (let i = 0; i < updatePayload.length; i += 2) {
-      const propKey = updatePayload[i];
-      const propValue = updatePayload[i + 1];
-      setProp(domElement, tag, propKey, propValue, nextProps);
-    }
+  for (let i = 0; i < updatePayload.length; i += 2) {
+    const propKey = updatePayload[i];
+    const propValue = updatePayload[i + 1];
+    setProp(domElement, tag, propKey, propValue, nextProps);
   }
 }
 
@@ -2048,6 +1871,18 @@ function diffHydratedGenericElement(
           warnForPropDifference(propKey, serverHTML, expectedHTML);
         }
         continue;
+      case 'className':
+        hydrateAttribute(domElement, propKey, 'class', value, extraAttributes);
+        continue;
+      case 'tabIndex':
+        hydrateAttribute(
+          domElement,
+          propKey,
+          'tabindex',
+          value,
+          extraAttributes,
+        );
+        continue;
       case 'style':
         extraAttributes.delete(propKey);
         diffHydratedStyles(domElement, value);
@@ -2244,828 +2079,132 @@ function diffHydratedGenericElement(
         );
         continue;
       }
-      // A few React string attributes have a different name.
-      // This is a mapping from React prop names to the attribute names.
-      case 'acceptCharset':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'accept-charset',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'className':
-        hydrateAttribute(domElement, propKey, 'class', value, extraAttributes);
-        continue;
-      case 'htmlFor':
-        hydrateAttribute(domElement, propKey, 'for', value, extraAttributes);
-        continue;
-      case 'httpEquiv':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'http-equiv',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'tabIndex':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'tabindex',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'crossOrigin':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'crossorigin',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'accentHeight':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'accent-height',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'alignmentBaseline':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'alignment-baseline',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'arabicForm':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'arabic-form',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'baselineShift':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'baseline-shift',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'capHeight':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'cap-height',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'clipPath':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'clip-path',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'clipRule':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'clip-rule',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'colorInterpolation':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'color-interpolation',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'colorInterpolationFilters':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'color-interpolation-filters',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'colorProfile':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'color-profile',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'colorRendering':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'color-rendering',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'dominantBaseline':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'dominant-baseline',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'enableBackground':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'enable-background',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'fillOpacity':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'fill-opacity',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'fillRule':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'fill-rule',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'floodColor':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'flood-color',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'floodOpacity':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'flood-opacity',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'fontFamily':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'font-family',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'fontSize':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'font-size',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'fontSizeAdjust':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'font-size-adjust',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'fontStretch':
+      case 'xHeight':
         hydrateAttribute(
           domElement,
           propKey,
-          'font-stretch',
+          'x-height',
           value,
           extraAttributes,
         );
         continue;
-      case 'fontStyle':
+      case 'xlinkActuate':
         hydrateAttribute(
           domElement,
           propKey,
-          'font-style',
+          'xlink:actuate',
           value,
           extraAttributes,
         );
         continue;
-      case 'fontVariant':
+      case 'xlinkArcrole':
         hydrateAttribute(
           domElement,
           propKey,
-          'font-variant',
+          'xlink:arcrole',
           value,
           extraAttributes,
         );
         continue;
-      case 'fontWeight':
+      case 'xlinkRole':
         hydrateAttribute(
           domElement,
           propKey,
-          'font-weight',
+          'xlink:role',
           value,
           extraAttributes,
         );
         continue;
-      case 'glyphName':
+      case 'xlinkShow':
         hydrateAttribute(
           domElement,
           propKey,
-          'glyph-name',
+          'xlink:show',
           value,
           extraAttributes,
         );
         continue;
-      case 'glyphOrientationHorizontal':
+      case 'xlinkTitle':
         hydrateAttribute(
           domElement,
           propKey,
-          'glyph-orientation-horizontal',
+          'xlink:title',
           value,
           extraAttributes,
         );
         continue;
-      case 'glyphOrientationVertical':
+      case 'xlinkType':
         hydrateAttribute(
           domElement,
           propKey,
-          'glyph-orientation-vertical',
+          'xlink:type',
           value,
           extraAttributes,
         );
         continue;
-      case 'horizAdvX':
+      case 'xmlBase':
         hydrateAttribute(
           domElement,
           propKey,
-          'horiz-adv-x',
+          'xml:base',
           value,
           extraAttributes,
         );
         continue;
-      case 'horizOriginX':
+      case 'xmlLang':
         hydrateAttribute(
           domElement,
           propKey,
-          'horiz-origin-x',
+          'xml:lang',
           value,
           extraAttributes,
         );
         continue;
-      case 'imageRendering':
+      case 'xmlSpace':
         hydrateAttribute(
           domElement,
           propKey,
-          'image-rendering',
+          'xml:space',
           value,
           extraAttributes,
         );
         continue;
-      case 'letterSpacing':
-        hydrateAttribute(
+      default: {
+        if (
+          // shouldIgnoreAttribute
+          // We have already filtered out null/undefined and reserved words.
+          propKey.length > 2 &&
+          (propKey[0] === 'o' || propKey[0] === 'O') &&
+          (propKey[1] === 'n' || propKey[1] === 'N')
+        ) {
+          continue;
+        }
+        const attributeName = getAttributeAlias(propKey);
+        let isMismatchDueToBadCasing = false;
+        let ownNamespaceDev = parentNamespaceDev;
+        if (ownNamespaceDev === HTML_NAMESPACE) {
+          ownNamespaceDev = getIntrinsicNamespace(tag);
+        }
+        if (ownNamespaceDev === HTML_NAMESPACE) {
+          extraAttributes.delete(attributeName.toLowerCase());
+        } else {
+          const standardName = getPossibleStandardName(propKey);
+          if (standardName !== null && standardName !== propKey) {
+            // If an SVG prop is supplied with bad casing, it will
+            // be successfully parsed from HTML, but will produce a mismatch
+            // (and would be incorrectly rendered on the client).
+            // However, we already warn about bad casing elsewhere.
+            // So we'll skip the misleading extra mismatch warning in this case.
+            isMismatchDueToBadCasing = true;
+            extraAttributes.delete(standardName);
+          }
+          extraAttributes.delete(attributeName);
+        }
+        const serverValue = getValueForAttribute(
           domElement,
-          propKey,
-          'letter-spacing',
+          attributeName,
           value,
-          extraAttributes,
         );
-        continue;
-      case 'lightingColor':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'lighting-color',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'markerEnd':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'marker-end',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'markerMid':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'marker-mid',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'markerStart':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'marker-start',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'overlinePosition':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'overline-position',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'overlineThickness':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'overline-thickness',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'paintOrder':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'paint-order',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'panose-1':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'panose-1',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'pointerEvents':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'pointer-events',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'renderingIntent':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'rendering-intent',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'shapeRendering':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'shape-rendering',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'stopColor':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stop-color',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'stopOpacity':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stop-opacity',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strikethroughPosition':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'strikethrough-position',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strikethroughThickness':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'strikethrough-thickness',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strokeDasharray':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stroke-dasharray',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strokeDashoffset':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stroke-dashoffset',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strokeLinecap':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stroke-linecap',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strokeLinejoin':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stroke-linejoin',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strokeMiterlimit':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stroke-miterlimit',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strokeOpacity':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stroke-opacity',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'strokeWidth':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'stroke-width',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'textAnchor':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'text-anchor',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'textDecoration':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'text-decoration',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'textRendering':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'text-rendering',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'transformOrigin':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'transform-origin',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'underlinePosition':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'underline-position',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'underlineThickness':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'underline-thickness',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'unicodeBidi':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'unicode-bidi',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'unicodeRange':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'unicode-range',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'unitsPerEm':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'units-per-em',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vAlphabetic':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'v-alphabetic',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vHanging':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'v-hanging',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vIdeographic':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'v-ideographic',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vMathematical':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'v-mathematical',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vectorEffect':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'vector-effect',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vertAdvY':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'vert-adv-y',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vertOriginX':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'vert-origin-x',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'vertOriginY':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'vert-origin-y',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'wordSpacing':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'word-spacing',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'writingMode':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'writing-mode',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xmlnsXlink':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xmlns:xlink',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xHeight':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'x-height',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xlinkActuate':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xlink:actuate',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xlinkArcrole':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xlink:arcrole',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xlinkRole':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xlink:role',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xlinkShow':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xlink:show',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xlinkTitle':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xlink:title',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xlinkType':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xlink:type',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xmlBase':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xml:base',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xmlLang':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xml:lang',
-          value,
-          extraAttributes,
-        );
-        continue;
-      case 'xmlSpace':
-        hydrateAttribute(
-          domElement,
-          propKey,
-          'xml:space',
-          value,
-          extraAttributes,
-        );
-        continue;
-      default: {
-        if (
-          // shouldIgnoreAttribute
-          // We have already filtered out null/undefined and reserved words.
-          propKey.length > 2 &&
-          (propKey[0] === 'o' || propKey[0] === 'O') &&
-          (propKey[1] === 'n' || propKey[1] === 'N')
-        ) {
-          continue;
-        }
-        let isMismatchDueToBadCasing = false;
-        let ownNamespaceDev = parentNamespaceDev;
-        if (ownNamespaceDev === HTML_NAMESPACE) {
-          ownNamespaceDev = getIntrinsicNamespace(tag);
-        }
-        if (ownNamespaceDev === HTML_NAMESPACE) {
-          extraAttributes.delete(propKey.toLowerCase());
-        } else {
-          const standardName = getPossibleStandardName(propKey);
-          if (standardName !== null && standardName !== propKey) {
-            // If an SVG prop is supplied with bad casing, it will
-            // be successfully parsed from HTML, but will produce a mismatch
-            // (and would be incorrectly rendered on the client).
-            // However, we already warn about bad casing elsewhere.
-            // So we'll skip the misleading extra mismatch warning in this case.
-            isMismatchDueToBadCasing = true;
-            extraAttributes.delete(standardName);
-          }
-          extraAttributes.delete(propKey);
-        }
-        const serverValue = getValueForAttribute(domElement, propKey, value);
         if (!isMismatchDueToBadCasing) {
           warnForPropDifference(propKey, serverValue, value);
         }

filename: packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js
patch: @@ -40,6 +40,7 @@ import {
 
 import isAttributeNameSafe from '../shared/isAttributeNameSafe';
 import isUnitlessNumber from '../shared/isUnitlessNumber';
+import getAttributeAlias from '../shared/getAttributeAlias';
 
 import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
 import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook';
@@ -640,23 +641,29 @@ function pushAttribute(
   value: string | boolean | number | Function | Object, // not null or undefined
 ): void {
   switch (name) {
+    // These are very common props and therefore are in the beginning of the switch.
+    // TODO: aria-label is a very common prop but allows booleans so is not like the others
+    // but should ideally go in this list too.
+    case 'className': {
+      pushStringAttribute(target, 'class', value);
+      break;
+    }
+    case 'tabIndex': {
+      pushStringAttribute(target, 'tabindex', value);
+      break;
+    }
+    case 'dir':
+    case 'role':
+    case 'viewBox':
+    case 'width':
+    case 'height': {
+      pushStringAttribute(target, name, value);
+      break;
+    }
     case 'style': {
       pushStyleAttribute(target, value);
       return;
     }
-    case 'defaultValue':
-    case 'defaultChecked': // These shouldn't be set as attributes on generic HTML elements.
-    case 'innerHTML': // Must use dangerouslySetInnerHTML instead.
-    case 'suppressContentEditableWarning':
-    case 'suppressHydrationWarning':
-      // Ignored. These are built-in to React on the client.
-      return;
-    case 'autoFocus':
-    case 'multiple':
-    case 'muted': {
-      pushBooleanAttribute(target, name.toLowerCase(), value);
-      return;
-    }
     case 'src':
     case 'href':
     case 'action':
@@ -709,6 +716,19 @@ function pushAttribute(
       );
       return;
     }
+    case 'defaultValue':
+    case 'defaultChecked': // These shouldn't be set as attributes on generic HTML elements.
+    case 'innerHTML': // Must use dangerouslySetInnerHTML instead.
+    case 'suppressContentEditableWarning':
+    case 'suppressHydrationWarning':
+      // Ignored. These are built-in to React on the client.
+      return;
+    case 'autoFocus':
+    case 'multiple':
+    case 'muted': {
+      pushBooleanAttribute(target, name.toLowerCase(), value);
+      return;
+    }
     case 'xlinkHref': {
       if (
         typeof value === 'function' ||
@@ -846,278 +866,33 @@ function pushAttribute(
       }
       return;
     }
-    // A few React string attributes have a different name.
-    // This is a mapping from React prop names to the attribute names.
-    case 'acceptCharset':
-      pushStringAttribute(target, 'accept-charset', value);
-      return;
-    case 'className':
-      pushStringAttribute(target, 'class', value);
-      return;
-    case 'htmlFor':
-      pushStringAttribute(target, 'for', value);
-      return;
-    case 'httpEquiv':
-      pushStringAttribute(target, 'http-equiv', value);
-      return;
-    // HTML and SVG attributes, but the SVG attribute is case sensitive.
-    case 'tabIndex':
-      pushStringAttribute(target, 'tabindex', value);
-      return;
-    case 'crossOrigin':
-      pushStringAttribute(target, 'crossorigin', value);
-      return;
-    // This is a list of all SVG attributes that need special casing.
-    // Regular attributes that just accept strings.
-    case 'accentHeight':
-      pushStringAttribute(target, 'accent-height', value);
-      return;
-    case 'alignmentBaseline':
-      pushStringAttribute(target, 'alignment-baseline', value);
-      return;
-    case 'arabicForm':
-      pushStringAttribute(target, 'arabic-form', value);
-      return;
-    case 'baselineShift':
-      pushStringAttribute(target, 'baseline-shift', value);
-      return;
-    case 'capHeight':
-      pushStringAttribute(target, 'cap-height', value);
-      return;
-    case 'clipPath':
-      pushStringAttribute(target, 'clip-path', value);
-      return;
-    case 'clipRule':
-      pushStringAttribute(target, 'clip-rule', value);
-      return;
-    case 'colorInterpolation':
-      pushStringAttribute(target, 'color-interpolation', value);
-      return;
-    case 'colorInterpolationFilters':
-      pushStringAttribute(target, 'color-interpolation-filters', value);
-      return;
-    case 'colorProfile':
-      pushStringAttribute(target, 'color-profile', value);
-      return;
-    case 'colorRendering':
-      pushStringAttribute(target, 'color-rendering', value);
-      return;
-    case 'dominantBaseline':
-      pushStringAttribute(target, 'dominant-baseline', value);
-      return;
-    case 'enableBackground':
-      pushStringAttribute(target, 'enable-background', value);
-      return;
-    case 'fillOpacity':
-      pushStringAttribute(target, 'fill-opacity', value);
-      return;
-    case 'fillRule':
-      pushStringAttribute(target, 'fill-rule', value);
-      return;
-    case 'floodColor':
-      pushStringAttribute(target, 'flood-color', value);
-      return;
-    case 'floodOpacity':
-      pushStringAttribute(target, 'flood-opacity', value);
-      return;
-    case 'fontFamily':
-      pushStringAttribute(target, 'font-family', value);
-      return;
-    case 'fontSize':
-      pushStringAttribute(target, 'font-size', value);
-      return;
-    case 'fontSizeAdjust':
-      pushStringAttribute(target, 'font-size-adjust', value);
-      return;
-    case 'fontStretch':
-      pushStringAttribute(target, 'font-stretch', value);
-      return;
-    case 'fontStyle':
-      pushStringAttribute(target, 'font-style', value);
-      return;
-    case 'fontVariant':
-      pushStringAttribute(target, 'font-variant', value);
-      return;
-    case 'fontWeight':
-      pushStringAttribute(target, 'font-weight', value);
-      return;
-    case 'glyphName':
-      pushStringAttribute(target, 'glyph-name', value);
-      return;
-    case 'glyphOrientationHorizontal':
-      pushStringAttribute(target, 'glyph-orientation-horizontal', value);
-      return;
-    case 'glyphOrientationVertical':
-      pushStringAttribute(target, 'glyph-orientation-vertical', value);
-      return;
-    case 'horizAdvX':
-      pushStringAttribute(target, 'horiz-adv-x', value);
-      return;
-    case 'horizOriginX':
-      pushStringAttribute(target, 'horiz-origin-x', value);
-      return;
-    case 'imageRendering':
-      pushStringAttribute(target, 'image-rendering', value);
-      return;
-    case 'letterSpacing':
-      pushStringAttribute(target, 'letter-spacing', value);
-      return;
-    case 'lightingColor':
-      pushStringAttribute(target, 'lighting-color', value);
-      return;
-    case 'markerEnd':
-      pushStringAttribute(target, 'marker-end', value);
-      return;
-    case 'markerMid':
-      pushStringAttribute(target, 'marker-mid', value);
-      return;
-    case 'markerStart':
-      pushStringAttribute(target, 'marker-start', value);
-      return;
-    case 'overlinePosition':
-      pushStringAttribute(target, 'overline-position', value);
-      return;
-    case 'overlineThickness':
-      pushStringAttribute(target, 'overline-thickness', value);
-      return;
-    case 'paintOrder':
-      pushStringAttribute(target, 'paint-order', value);
-      return;
-    case 'panose-1':
-      pushStringAttribute(target, 'panose-1', value);
-      return;
-    case 'pointerEvents':
-      pushStringAttribute(target, 'pointer-events', value);
-      return;
-    case 'renderingIntent':
-      pushStringAttribute(target, 'rendering-intent', value);
-      return;
-    case 'shapeRendering':
-      pushStringAttribute(target, 'shape-rendering', value);
-      return;
-    case 'stopColor':
-      pushStringAttribute(target, 'stop-color', value);
-      return;
-    case 'stopOpacity':
-      pushStringAttribute(target, 'stop-opacity', value);
-      return;
-    case 'strikethroughPosition':
-      pushStringAttribute(target, 'strikethrough-position', value);
-      return;
-    case 'strikethroughThickness':
-      pushStringAttribute(target, 'strikethrough-thickness', value);
-      return;
-    case 'strokeDasharray':
-      pushStringAttribute(target, 'stroke-dasharray', value);
-      return;
-    case 'strokeDashoffset':
-      pushStringAttribute(target, 'stroke-dashoffset', value);
-      return;
-    case 'strokeLinecap':
-      pushStringAttribute(target, 'stroke-linecap', value);
-      return;
-    case 'strokeLinejoin':
-      pushStringAttribute(target, 'stroke-linejoin', value);
-      return;
-    case 'strokeMiterlimit':
-      pushStringAttribute(target, 'stroke-miterlimit', value);
-      return;
-    case 'strokeOpacity':
-      pushStringAttribute(target, 'stroke-opacity', value);
-      return;
-    case 'strokeWidth':
-      pushStringAttribute(target, 'stroke-width', value);
-      return;
-    case 'textAnchor':
-      pushStringAttribute(target, 'text-anchor', value);
-      return;
-    case 'textDecoration':
-      pushStringAttribute(target, 'text-decoration', value);
-      return;
-    case 'textRendering':
-      pushStringAttribute(target, 'text-rendering', value);
-      return;
-    case 'transformOrigin':
-      pushStringAttribute(target, 'transform-origin', value);
-      return;
-    case 'underlinePosition':
-      pushStringAttribute(target, 'underline-position', value);
-      return;
-    case 'underlineThickness':
-      pushStringAttribute(target, 'underline-thickness', value);
-      return;
-    case 'unicodeBidi':
-      pushStringAttribute(target, 'unicode-bidi', value);
-      return;
-    case 'unicodeRange':
-      pushStringAttribute(target, 'unicode-range', value);
-      return;
-    case 'unitsPerEm':
-      pushStringAttribute(target, 'units-per-em', value);
-      return;
-    case 'vAlphabetic':
-      pushStringAttribute(target, 'v-alphabetic', value);
-      return;
-    case 'vHanging':
-      pushStringAttribute(target, 'v-hanging', value);
-      return;
-    case 'vIdeographic':
-      pushStringAttribute(target, 'v-ideographic', value);
-      return;
-    case 'vMathematical':
-      pushStringAttribute(target, 'v-mathematical', value);
-      return;
-    case 'vectorEffect':
-      pushStringAttribute(target, 'vector-effect', value);
-      return;
-    case 'vertAdvY':
-      pushStringAttribute(target, 'vert-adv-y', value);
-      return;
-    case 'vertOriginX':
-      pushStringAttribute(target, 'vert-origin-x', value);
-      return;
-    case 'vertOriginY':
-      pushStringAttribute(target, 'vert-origin-y', value);
-      return;
-    case 'wordSpacing':
-      pushStringAttribute(target, 'word-spacing', value);
-      return;
-    case 'writingMode':
-      pushStringAttribute(target, 'writing-mode', value);
-      return;
-    case 'xmlnsXlink':
-      pushStringAttribute(target, 'xmlns:xlink', value);
-      return;
-    case 'xHeight':
-      pushStringAttribute(target, 'x-height', value);
-      return;
     case 'xlinkActuate':
       pushStringAttribute(target, 'xlink:actuate', value);
-      break;
+      return;
     case 'xlinkArcrole':
       pushStringAttribute(target, 'xlink:arcrole', value);
-      break;
+      return;
     case 'xlinkRole':
       pushStringAttribute(target, 'xlink:role', value);
-      break;
+      return;
     case 'xlinkShow':
       pushStringAttribute(target, 'xlink:show', value);
-      break;
+      return;
     case 'xlinkTitle':
       pushStringAttribute(target, 'xlink:title', value);
-      break;
+      return;
     case 'xlinkType':
       pushStringAttribute(target, 'xlink:type', value);
-      break;
+      return;
     case 'xmlBase':
       pushStringAttribute(target, 'xml:base', value);
-      break;
+      return;
     case 'xmlLang':
       pushStringAttribute(target, 'xml:lang', value);
-      break;
+      return;
     case 'xmlSpace':
       pushStringAttribute(target, 'xml:space', value);
-      break;
+      return;
     default:
       if (
         // shouldIgnoreAttribute
@@ -1129,22 +904,23 @@ function pushAttribute(
         return;
       }
 
-      if (isAttributeNameSafe(name)) {
+      const attributeName = getAttributeAlias(name);
+      if (isAttributeNameSafe(attributeName)) {
         // shouldRemoveAttribute
         switch (typeof value) {
           case 'function':
           case 'symbol': // eslint-disable-line
             return;
           case 'boolean': {
-            const prefix = name.toLowerCase().slice(0, 5);
+            const prefix = attributeName.toLowerCase().slice(0, 5);
             if (prefix !== 'data-' && prefix !== 'aria-') {
               return;
             }
           }
         }
         target.push(
           attributeSeparator,
-          stringToChunk(name),
+          stringToChunk(attributeName),
           attributeAssign,
           stringToChunk(escapeTextForBrowser(value)),
           attributeEnd,
@@ -2862,6 +2638,16 @@ export function pushStartInstance(
   }
 
   switch (type) {
+    case 'div':
+    case 'span':
+    case 'svg':
+    case 'path':
+    case 'a':
+    case 'g':
+    case 'p':
+    case 'li':
+      // Fast track very common tags
+      break;
     // Special tags
     case 'select':
       return pushStartSelect(target, props);
@@ -2971,15 +2757,14 @@ export function pushStartInstance(
       );
     }
     default: {
-      if (type.indexOf('-') === -1) {
-        // Generic element
-        return pushStartGenericElement(target, props, type);
-      } else {
+      if (type.indexOf('-') !== -1) {
         // Custom element
         return pushStartCustomElement(target, props, type);
       }
     }
   }
+  // Generic element
+  return pushStartGenericElement(target, props, type);
 }
 
 const endTag1 = stringToPrecomputedChunk('</');

filename: packages/react-dom-bindings/src/shared/getAttributeAlias.js
patch: @@ -0,0 +1,96 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+const aliases = new Map([
+  ['acceptCharset', 'accept-charset'],
+  ['htmlFor', 'for'],
+  ['httpEquiv', 'http-equiv'],
+  // HTML and SVG attributes, but the SVG attribute is case sensitive.],
+  ['crossOrigin', 'crossorigin'],
+  // This is a list of all SVG attributes that need special casing.
+  // Regular attributes that just accept strings.],
+  ['accentHeight', 'accent-height'],
+  ['alignmentBaseline', 'alignment-baseline'],
+  ['arabicForm', 'arabic-form'],
+  ['baselineShift', 'baseline-shift'],
+  ['capHeight', 'cap-height'],
+  ['clipPath', 'clip-path'],
+  ['clipRule', 'clip-rule'],
+  ['colorInterpolation', 'color-interpolation'],
+  ['colorInterpolationFilters', 'color-interpolation-filters'],
+  ['colorProfile', 'color-profile'],
+  ['colorRendering', 'color-rendering'],
+  ['dominantBaseline', 'dominant-baseline'],
+  ['enableBackground', 'enable-background'],
+  ['fillOpacity', 'fill-opacity'],
+  ['fillRule', 'fill-rule'],
+  ['floodColor', 'flood-color'],
+  ['floodOpacity', 'flood-opacity'],
+  ['fontFamily', 'font-family'],
+  ['fontSize', 'font-size'],
+  ['fontSizeAdjust', 'font-size-adjust'],
+  ['fontStretch', 'font-stretch'],
+  ['fontStyle', 'font-style'],
+  ['fontVariant', 'font-variant'],
+  ['fontWeight', 'font-weight'],
+  ['glyphName', 'glyph-name'],
+  ['glyphOrientationHorizontal', 'glyph-orientation-horizontal'],
+  ['glyphOrientationVertical', 'glyph-orientation-vertical'],
+  ['horizAdvX', 'horiz-adv-x'],
+  ['horizOriginX', 'horiz-origin-x'],
+  ['imageRendering', 'image-rendering'],
+  ['letterSpacing', 'letter-spacing'],
+  ['lightingColor', 'lighting-color'],
+  ['markerEnd', 'marker-end'],
+  ['markerMid', 'marker-mid'],
+  ['markerStart', 'marker-start'],
+  ['overlinePosition', 'overline-position'],
+  ['overlineThickness', 'overline-thickness'],
+  ['paintOrder', 'paint-order'],
+  ['panose-1', 'panose-1'],
+  ['pointerEvents', 'pointer-events'],
+  ['renderingIntent', 'rendering-intent'],
+  ['shapeRendering', 'shape-rendering'],
+  ['stopColor', 'stop-color'],
+  ['stopOpacity', 'stop-opacity'],
+  ['strikethroughPosition', 'strikethrough-position'],
+  ['strikethroughThickness', 'strikethrough-thickness'],
+  ['strokeDasharray', 'stroke-dasharray'],
+  ['strokeDashoffset', 'stroke-dashoffset'],
+  ['strokeLinecap', 'stroke-linecap'],
+  ['strokeLinejoin', 'stroke-linejoin'],
+  ['strokeMiterlimit', 'stroke-miterlimit'],
+  ['strokeOpacity', 'stroke-opacity'],
+  ['strokeWidth', 'stroke-width'],
+  ['textAnchor', 'text-anchor'],
+  ['textDecoration', 'text-decoration'],
+  ['textRendering', 'text-rendering'],
+  ['transformOrigin', 'transform-origin'],
+  ['underlinePosition', 'underline-position'],
+  ['underlineThickness', 'underline-thickness'],
+  ['unicodeBidi', 'unicode-bidi'],
+  ['unicodeRange', 'unicode-range'],
+  ['unitsPerEm', 'units-per-em'],
+  ['vAlphabetic', 'v-alphabetic'],
+  ['vHanging', 'v-hanging'],
+  ['vIdeographic', 'v-ideographic'],
+  ['vMathematical', 'v-mathematical'],
+  ['vectorEffect', 'vector-effect'],
+  ['vertAdvY', 'vert-adv-y'],
+  ['vertOriginX', 'vert-origin-x'],
+  ['vertOriginY', 'vert-origin-y'],
+  ['wordSpacing', 'word-spacing'],
+  ['writingMode', 'writing-mode'],
+  ['xmlnsXlink', 'xmlns:xlink'],
+  ['xHeight', 'x-height'],
+]);
+
+export default function (name: string): string {
+  return aliases.get(name) || name;
+}

filename: packages/react-dom-bindings/src/shared/isUnitlessNumber.js
patch: @@ -10,81 +10,79 @@
 /**
  * CSS properties which accept numbers but are not in units of "px".
  */
+const unitlessNumbers = new Set([
+  'animationIterationCount',
+  'aspectRatio',
+  'borderImageOutset',
+  'borderImageSlice',
+  'borderImageWidth',
+  'boxFlex',
+  'boxFlexGroup',
+  'boxOrdinalGroup',
+  'columnCount',
+  'columns',
+  'flex',
+  'flexGrow',
+  'flexPositive',
+  'flexShrink',
+  'flexNegative',
+  'flexOrder',
+  'gridArea',
+  'gridRow',
+  'gridRowEnd',
+  'gridRowSpan',
+  'gridRowStart',
+  'gridColumn',
+  'gridColumnEnd',
+  'gridColumnSpan',
+  'gridColumnStart',
+  'fontWeight',
+  'lineClamp',
+  'lineHeight',
+  'opacity',
+  'order',
+  'orphans',
+  'scale',
+  'tabSize',
+  'widows',
+  'zIndex',
+  'zoom',
+  'fillOpacity', // SVG-related properties
+  'floodOpacity',
+  'stopOpacity',
+  'strokeDasharray',
+  'strokeDashoffset',
+  'strokeMiterlimit',
+  'strokeOpacity',
+  'strokeWidth',
+  'MozAnimationIterationCount', // Known Prefixed Properties
+  'MozBoxFlex', // TODO: Remove these since they shouldn't be used in modern code
+  'MozBoxFlexGroup',
+  'MozLineClamp',
+  'msAnimationIterationCount',
+  'msFlex',
+  'msZoom',
+  'msFlexGrow',
+  'msFlexNegative',
+  'msFlexOrder',
+  'msFlexPositive',
+  'msFlexShrink',
+  'msGridColumn',
+  'msGridColumnSpan',
+  'msGridRow',
+  'msGridRowSpan',
+  'WebkitAnimationIterationCount',
+  'WebkitBoxFlex',
+  'WebKitBoxFlexGroup',
+  'WebkitBoxOrdinalGroup',
+  'WebkitColumnCount',
+  'WebkitColumns',
+  'WebkitFlex',
+  'WebkitFlexGrow',
+  'WebkitFlexPositive',
+  'WebkitFlexShrink',
+  'WebkitLineClamp',
+]);
 export default function (name: string): boolean {
-  switch (name) {
-    case 'animationIterationCount':
-    case 'aspectRatio':
-    case 'borderImageOutset':
-    case 'borderImageSlice':
-    case 'borderImageWidth':
-    case 'boxFlex':
-    case 'boxFlexGroup':
-    case 'boxOrdinalGroup':
-    case 'columnCount':
-    case 'columns':
-    case 'flex':
-    case 'flexGrow':
-    case 'flexPositive':
-    case 'flexShrink':
-    case 'flexNegative':
-    case 'flexOrder':
-    case 'gridArea':
-    case 'gridRow':
-    case 'gridRowEnd':
-    case 'gridRowSpan':
-    case 'gridRowStart':
-    case 'gridColumn':
-    case 'gridColumnEnd':
-    case 'gridColumnSpan':
-    case 'gridColumnStart':
-    case 'fontWeight':
-    case 'lineClamp':
-    case 'lineHeight':
-    case 'opacity':
-    case 'order':
-    case 'orphans':
-    case 'scale':
-    case 'tabSize':
-    case 'widows':
-    case 'zIndex':
-    case 'zoom':
-    case 'fillOpacity': // SVG-related properties
-    case 'floodOpacity':
-    case 'stopOpacity':
-    case 'strokeDasharray':
-    case 'strokeDashoffset':
-    case 'strokeMiterlimit':
-    case 'strokeOpacity':
-    case 'strokeWidth':
-    case 'MozAnimationIterationCount': // Known Prefixed Properties
-    case 'MozBoxFlex': // TODO: Remove these since they shouldn't be used in modern code
-    case 'MozBoxFlexGroup':
-    case 'MozLineClamp':
-    case 'msAnimationIterationCount':
-    case 'msFlex':
-    case 'msZoom':
-    case 'msFlexGrow':
-    case 'msFlexNegative':
-    case 'msFlexOrder':
-    case 'msFlexPositive':
-    case 'msFlexShrink':
-    case 'msGridColumn':
-    case 'msGridColumnSpan':
-    case 'msGridRow':
-    case 'msGridRowSpan':
-    case 'WebkitAnimationIterationCount':
-    case 'WebkitBoxFlex':
-    case 'WebKitBoxFlexGroup':
-    case 'WebkitBoxOrdinalGroup':
-    case 'WebkitColumnCount':
-    case 'WebkitColumns':
-    case 'WebkitFlex':
-    case 'WebkitFlexGrow':
-    case 'WebkitFlexPositive':
-    case 'WebkitFlexShrink':
-    case 'WebkitLineClamp':
-      return true;
-    default:
-      return false;
-  }
+  return unitlessNumbers.has(name);
 }