@expo/html-elements
TypeScript icon, indicating that this package has built-in type declarations

0.9.1 • Public • Published

@expo/html-elements

Product: demo

Supports Expo iOS Supports Expo Android Supports Expo Web Circle CI

Simple, light-weight, and well tested, universal semantic HTML elements as React components for iOS, Android, web, and desktop apps!

We at Expo recommend using platform agnostic primitives like View, Image, and Text whenever possible but sometimes that's not easy. Some primitives like Tables, and Footers are native to web only and currently have no way of easily accessing. This package aims to solve that while still being an optimal UI package for iOS, and Android.

What you get

  • Using @expo/html-elements will optimize your website for SEO and accessibility. Meaning your websites are indexed more accurately and your native apps better accommodate physically impaired users.
    • This package takes full advantage of react-native-web a11y rules whenever possible.
    • For example, the H1 component will render an <h1 /> on web, a UILabel on iOS, and a TextView on Android.
  • Every component can accept styles from the StyleSheet API.
  • TypeScript works for iOS, Android, and web, no more having to create monkey patches to use href on a Text element.
  • Every component is tested render tested universally for iOS, Android, and Web using the package jest-expo-enzyme. Each element is also E2E tested on iOS with Detox, and web with jest-expo-puppeteer.
  • This package is completely side-effect free!

Setup

Install:

yarn add @expo/html-elements

Import and use the package:

import { H1 } from '@expo/html-elements';

Components

Here is a list of all the currently supported elements and the web feature they map to. Not all HTML elements are supported. There are some HTML elements that mostly overlap with some universal modules, you should always try to use the universal modules whenever possible. All supported components are a capitalized variation of the semantic HTML they implement/emulate.

HTML @expo/html-elements
<a /> <A />
<article /> <Article />
<aside /> <Aside />
<b /> <B />
<blockquote /> <BlockQuote />
<br /> <BR />
<caption /> <Caption />
<code /> <Code />
<del /> <Del />
<div /> <Div />
<em /> <EM />
<footer /> <Footer />
<h1 /> <H1 />
<h2 /> <H2 />
<h3 /> <H3 />
<h4 /> <H4 />
<h5 /> <H5 />
<h6 /> <H6 />
<header /> <Header />
<hr /> <HR />
<i /> <I />
<main /> <Main />
<mark /> <Mark />
<nav /> <Nav />
<p /> <P />
<pre /> <Pre />
<q /> <Q />
<s /> <S />
<section /> <Section />
<span /> <Span />
<strong /> <Strong />
<table /> <Table />
<tbody /> <TBody />
<td /> <TD />
<tfoot /> <TFoot />
<th /> <TH />
<thead /> <THead />
<time /> <Time />
<tr /> <TR />
<ul /> <UL />
<li /> <LI />
<details /> ⏱ Pending
<summary /> ⏱ Pending
<progress /> ⏱ Pending
<select /> ⏱ Pending
<picture /> ⏱ Pending
<figure /> ⏱ Pending
<figcaption /> ⏱ Pending
<form /> ⏱ Pending
<label /> ⏱ Pending

External

Other features not implemented in this package can be found in different parts of the Expo ecosystem.

HTML Universal Package
<audio /> Audio expo-av
<button /> <Button /> react-native
<input type="text" /> <TextInput /> react-native
<input type="file" /> ImagePicker expo-image-picker
<input type="file" /> DocumentPicker expo-document-picker
<canvas /> <GLView /> expo-gl & Expo Canvas
<iframe /> <WebView /> <WebView />. @react-native-community/web-view is not maintained by Expo and doesn't have web support
<link /> None Eject the index.html with npx expo customize and link resources directly with <link />
<noscript /> None Eject the index.html with npx expo customize and use <noscript /> directly as HTML
<div /> <View /> react-native
<img /> <Image /> react-native
<span /> <Text /> react-native
<video /> <Video /> expo-av
style="backdrop-filter" <BlurView /> expo-blur
style="linear-gradient()" <LinearGradient /> expo-linear-gradient

Headings

Header elements will use the expected font size and margins from web universally. You can see how the native CSS units (rem, and em) are transformed in css/units.

import { H1, H2, H3, H4, H5, H6 } from '@expo/html-elements';

<H1/>

import { H1 } from '@expo/html-elements';
export default () => <H1>Example<H1/>
Platform Output
Web <h1 aria-level="1" dir="auto" role="heading" />
Native <Text role="header" style={[Custom]} />

<H2/>

import { H2 } from '@expo/html-elements';
export default () => <H2>Example<H2/>
Platform Output
Web <h2 aria-level="2" dir="auto" role="heading" />
Native <Text role="header" style={[Custom]} />

<H3/>

import { H3 } from '@expo/html-elements';
export default () => <H3>Example<H3/>
Platform Output
Web <h3 aria-level="3" dir="auto" role="heading" />
Native <Text role="header" style={[Custom]} />

<H4/>

import { H4 } from '@expo/html-elements';
export default () => <H4>Example<H4/>
Platform Output
Web <h4 aria-level="4" dir="auto" role="heading" />
Native <Text role="header" style={[Custom]} />

<H5/>

import { H5 } from '@expo/html-elements';
export default () => <H5>Example<H5/>
Platform Output
Web <h5 aria-level="5" dir="auto" role="heading" />
Native <Text role="header" style={[Custom]} />

<H6/>

import { H6 } from '@expo/html-elements';
export default () => <H6>Example<H6/>
Platform Output
Web <h6 aria-level="6" dir="auto" role="heading" />
Native <Text role="header" style={[Custom]} />

Link

<A/>

You can use the anchor element with href prop to open links. On native this will attempt to use the Linking API to open the href.

  • The CSS style is fully normalized to match <Text />
  • For pseudo-class effects like hover and focus states check out the package react-native-web-hooks | tutorial
import { A } from '@expo/html-elements';

export default () => <A href="#" target="_blank" />;
}
Platform Output
Web <a data-focusable="{true}" dir="auto" href="#" role="link" target="_blank" />
Native <Text role="link" onPress={[Function]} />

Layout

You can use layout elements like Header, Main, Footer, Section, Nav, etc. as a drop-in replacement for Views in your existing app.

Default Layout style

All layout HTML elements inherit the shared style of <View /> to accommodate the Yoga layout engine which we use on native for iOS, and Android.

  • display is always flex. This is because Yoga only implements display: flex.
  • flex-direction is always column instead of row.

Why use Layout elements

Consider the following: in your app you have a basic element at the top which wraps the buttons and title. A screen reader doesn't understand that this is a header, and mostly neither does a web crawler. But if you replace the encasing view with a <Header /> the following happens:

Some elements like Footer and Main have no iOS, or Android enhancements, but they'll still improve web. Using the proper HTML 5 elements will make your layout compliant with the HTML5 outline algorithm.

<Div/>

Renders a <div /> on web and a View with no ARIA set on mobile.

import { Div } from '@expo/html-elements';

export default () => (
    <Div>
      <P>Some content in the main element</P>
    </Div>
  );
)
Platform Output
Web <div />
Native <View />

<Nav/>

import { Nav } from '@expo/html-elements';

export default () => <Nav />;
Platform Output
Web <nav style="display:flex" />
Native <View />

<Header/>

Renders a <header /> on web with ARIA set to banner and a View with ARIA set to header on mobile.

import { Header } from '@expo/html-elements';

export default () => <Header />;
Platform Output
Web <header role="banner" />
Native <View />
iOS UIView uses UIAccessibilityTraitHeader.
Android View will use the proper AccessibilityNodeInfoCompat.CollectionItemInfoCompat docs

<Main/>

Renders a <main /> on web with ARIA role set to main and a View with no ARIA set on mobile.

import { Main } from '@expo/html-elements';

export default () => (
    <Main>
      <P>Some content in the main element</P>
    </Main>
  );
)
Platform Output
Web <main role="main" style="display:flex" />
Native <View />

<Section/>

Renders a <section /> on web with ARIA set to region and a View with ARIA set to summary on mobile.

import { Section } from '@expo/html-elements';

export default () => <Section />;
Platform Output
Web <section role="region" style="display:flex" />
Native <View role="summary" />

<Article/>

Renders an <article /> on web and a View everywhere else.

import { Article } from '@expo/html-elements';

export default () => <Article />;
Platform Output
Web <article role="article" />
Native <View />

<Aside/>

import { Aside } from '@expo/html-elements';

export default () => <Aside />;
Platform Output
Web <aside role="complementary" />
Native <View />

<Footer/>

Renders an <footer /> on web and a View everywhere else.

import { Footer } from '@expo/html-elements';

export default () => <Footer />;
Platform Output
Web <footer role="contentinfo" />
Native <View />

Text

Text elements currently use Text universally rendering either a div or span to emulate Yoga style properly.

  • Style is modified to match web.
  • All font styles are reset (minus Code, and Pre).
  • All elements accept styles from StyleSheet API.
import { P, B, S, I, BR, Code } from '@expo/html-elements';

export default () => (
  <>
    <P>
      Hello<B>World (in bold)</B>
    </P>
    <S>strike text</S>
    <BR />
    <I>Italic</I>
    <Code>const foo = true</Code>
  </>
);

<P/>

Standard paragraph element.

Platform Output
Universal <Text style={{ fontSize: 14, marginVertical: '1em' }} />

<B/>

Bold text text.

Platform Output
Universal <Text style={{ fontWeight: 'bold' }} />

<Strong/>

Alternate bold text.

Platform Output
Universal <Text style={{ fontWeight: 'bold' }} />

<Span/>

Inline text element.

Platform Output
Universal <Text />

<S/>

Strike through text.

Platform Output
Universal <Text style={{ textDecorationLine: 'line-through' }} />

<Del/>

Alternate strike through text.

Platform Output
Universal <Text style={{ textDecorationLine: 'line-through' }} />

<I/>

Italic text.

Platform Output
Universal <Text style={{ fontStyle: 'italic' }} />

<EM/>

Alternate italic text.

Platform Output
Universal <Text style={{ fontStyle: 'italic' }} />

<Code/>

Inline code block with fontFamily: 'Courier' on iOS and Web, fontFamily: 'monospace' on Android.

Platform Output
Universal <Text style={[Custom]} />

<Pre/>

Render a preformatted code block with fontFamily: 'Courier' on iOS and Web, fontFamily: 'monospace' on Android.

<Pre>{`
body {
  color: red;
}
`}</Pre>

// Or pass views

<Pre>
  <Code>{`const val = true`}</Code>
</Pre>
Platform Output
Universal <Text style={[Custom]} /> | <View />

<Mark/>

Highlight text.

Platform Output
Universal <Text style={{ backgroundColor: 'yellow', color: 'black' }} />

<Q/>

Quoted text.

Platform Output
Universal <Text style={[Custom]}>"{props.children}"</Text>

<BlockQuote/>

Platform Output
Universal <View style={[Custom]} />

<Time/>

  • dateTime prop is supported on web and stripped on native.
Platform Output
Universal <Text style={[Custom]} />

Lists

Lists can be used to create basic bulleted or numbered lists. You should try and use universal FlatList or SectionList components for long scrolling lists instead of these.

<UL/>

Create an unordered (bulleted) list <ul /> on web, and emulates the style with a <View /> on native.

  • [x] Resets font styles everywhere.
  • [ ] Supports i18n by reversing format on iOS and Android
  • [ ] Supports custom bullets
import { UL, LI } from '@expo/html-elements';

export default () => (
  <UL>
    <LI>oranges</LI>
    <LI>apples</LI>
    <UL>
      <LI>green</LI>
      <LI>red</LI>
    </UL>
  </UL>
);
Platform Output
Web <ul />
Native <View style={[Custom]} />

<LI/>

Create a standard list item <li /> on web and a native view on mobile which can render text or views inside it.

Platform Output
Web <li />
Native <Text style={[Custom]} /> | <View style={[Custom]} />

Rules

<HR/>

Renders a <View> everywhere. Style is modified to match web.

import { HR } from '@expo/html-elements';

export default () => <HR />;
Platform Output
Web <hr />
Native <View style={[Custom]} />

<BR/>

Create a line break.

Platform Output
Web <br />
Native <View style={{height: 8, width: 0}} />

Tables

Create tables universally.

  • Each element renders to the expected type on web.
  • padding is removed from all table elements.
  • Text can only be rendered in TH and TD on mobile.
  • colSpan and rowSpan are currently web-only (PRs welcome).
import { Table, THead, TH, TBody, TFoot, TR, TD, Caption } from '@expo/html-elements';
import { Text } from 'react-native';

export default () => (
  <Table>
    <Caption>Caption</Caption>
    <THead>
      <TR>
        <TH colSpan="2">The table header</TH>
      </TR>
    </THead>
    <TBody>
      <TR>
        <TD>The table body</TD>
        <TD>with two columns</TD>
      </TR>
    </TBody>
    <TFoot>
      <TR>
        <TD>
          <Text>This is the table footer</Text>
        </TD>
      </TR>
    </TFoot>
  </Table>
);

Table example output web

<table>
  <caption>
    Caption
  </caption>
  <thead>
    <tr>
      <th colspan="2">The table header</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>The table body</td>
      <td>with two columns</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td><div>The table body</div></td>
    </tr>
  </tfoot>
</table>

<Table/>

Base element for creating a Table.

Platform Output
Web <table />
Native <View style={[Custom]} />

<THead/>

Header element in a Table.

Platform Output
Web <thead />
Native <View style={[Custom]} />

<TBody/>

Body element in a Table.

Platform Output
Web <tbody />
Native <View style={[Custom]} />

<TFoot/>

Footer element in a Table.

Platform Output
Web <tfoot />
Native <View style={[Custom]} />

<TH/>

Used to display text in the Header.

  • colSpan and rowSpan are currently web-only.
Platform Output
Web <th />
Native <Text style={[Custom]} />

<TR/>

Used to create a Row in a Table.

Platform Output
Web <tr />
Native <View style={[Custom]} />

<TD/>

Create a cell in a Table.

  • colSpan and rowSpan are currently web-only.
Platform Output
Web <td />
Native <View style={[Custom]} />

<Caption/>

Used to caption your table. Excepts text as a child.

Platform Output
Web <caption />
Native <Text style={[Custom]} />

TODO

  • Improve relative imports for better tree-shaking.

Babel

You can write react-dom elements in your code and use the babel plugin to transform them to @expo/html-elements elements.

// babel.config.js
module.exports = {
  plugins: ['@expo/html-elements/babel'],
};

Input

export default function Page() {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

Output

The import is automatically added if it's not already present. All props are passed through without any additional transforms.

import { Div, H1 } from '@expo/html-elements';

export default function Page() {
  return (
    <Div>
      <H1>Hello World</H1>
    </Div>
  );
}

Contributing

Contributions are very welcome! Please refer to guidelines described in the contributing guide.

Package Sidebar

Install

npm i @expo/html-elements

Weekly Downloads

23,296

Version

0.9.1

License

MIT

Unpacked Size

153 kB

Total Files

94

Last publish

Collaborators

  • szdziedzic
  • alanhughes
  • kadikraman
  • radoslawkrzemien
  • aleqsio
  • marklawlor
  • gabrieldonadel
  • simek
  • keith-kurak
  • christopherwalter
  • kbrandwijk
  • fiber-god
  • ide
  • brentvatne
  • evanbacon
  • quinlanj
  • expoadmin
  • tsapeta
  • exponent
  • wschurman
  • wkozyra
  • bycedric
  • lukmccall
  • jonsamp
  • princefleaswallow
  • kudochien