Hi, I'm Atomico js and I bring you the React syntax for webcomponents, I think you and I get along very well 😊.
First let's say that Atomico is light since it has a size close to 3kB vs React + ReactDOM that have a size close to 60kB, now if your project is already written in React I can integrate Atomico progressively since a component created can be instantiated as a component for React thanks to @atomico/react, example:
Magical 🪄, isn't it?... well now let's speed up your Atomico learning path:
Atomico, like React, allows a declaration of components using only functions, example:
From the example we will highlight the following differences:
In Atomico you only use one import.
useProp is like useState, but with the difference that useProp references the state from the webcomponent property defined in counter.props.
const props allows us to create the properties of our webcomponent, these are like React's propTypes, but with a big difference they are associated with the instance and can be read and modified by referencing the node, example
Now I want to invite you to learn how to declare a style using Atomico.
It is common to see the use of libraries such as Emotion or styled-components to encapsulate styles in React, but these add an additional cost, be it for performance or bundle, in Atomico there is no such cost.
It is normal for React to create components that you then instantiate within other components, for example:
with Atomico there are certain differences:
The constructor in Atomic is the product of the c function and is the one you will use to register your webcomponent, example:
According to the previous example, you can instantiate MyComponent as a JSX Component, example:
This instance type allows autocompletion at the JSX level and type validation at the Typescript level.
This will be useful for reusing templates, but always remember stateless.
import { Button } from "@formas/button/react";
function App(){
return <>
<h1>React App!</h1>
<Button onClick={()=>console.log("Click!")}>
Submit
</Button>
</>
}document.querySelector("my-counter").count = 10;ReactDom.render needs a reference to mount the component, in Atomico you only need to create the my-counter tag to create a new instance of the component.
The <host/> tag is similar to <> </> for React, but <host/> represents the webcomponent instance and every component created with Atomico must return the host tag
This is only readability, but in Atomico by convention we do not use capital letters when naming our component, these are only used when creating the customElement as in line 16, since Counter is instantiable.
import { useState } from "react";
import ReactDOM from 'react-dom'
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
render(
<Counter initialCount={1}/>,
document.querySelector("#counter")
);import { c, useProp } from "atomico";
const props = { count: { type: Number, value: 0 } };
const Counter = c(
() => {
const [count, setCount] = useProp("count");
return (
<host>
Count: {count}
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
</host>
);
},
{ props }
);
customElements.define("my-counter", Counter);const Button = styled.a`
/* This renders the buttons above... Edit me! */
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
/* The GitHub button is a primary button
* edit this to target it specifically! */
${props => props.primary && css`
background: white;
color: black;
`}
`
render(
<div>
<Button
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
primary
>
GitHub
</Button>
<Button as={Link} href="/docs">
Documentation
</Button>
</div>
)import { c, css } from "atomico";
const props = { primary: { type: Boolean, relfect: true } };
const styles = css`
:host {
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
}
:host([primary]) {
background: white;
color: black;
}
`;
export const Button = c(
() => (
<host shadowDom>
<slot />
</host>
),
{ props, styles }
);
customElements.define("my-button", Button);
function Child({children}){
return <span>children</span>
}
function Main(){
return <>
<Child>text 1...</Child>
<Child>text 2...</Child>
</>
}import { c } from "atomico";
export const MyComponent = c(() => <host>...</host>); // Constructor
customElements.define("my-component", MyComponent);import { c } from "atomico";
import { MyComponent } from "./my-component";
export const MyApp = c(() => (
<host>
<MyComponent />
</host>
));
customElements.define("my-app", MyApp);function MyIcon({ size }) {
return (
<svg height={size} width={size}>
<circle r="45" cx="50" cy="50" fill="red" />
</svg>
);
}
const MyComponent = c(() => (
<host>
Small <MyIcon size={"1rem"} />
Large <MyIcon size={"2rem"} />
</host>
));