--- id: what-is title: What are NEAR Components? sidebar_label: What is a Component? --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import {WidgetEditor} from "@site/src/components/widget-editor" NEAR Components are a new way to build web applications. They are composable, reusable and decentralized. ![img](/docs/assets/welcome-pages/bos-landing.png) :::tip You can login to interact with the examples in this section. ::: --- ## Familiar to Web Developers NEAR Components are built on top of [React Components](https://react.dev/), meaning that they: - Handle input through the `props` variable - Handle state through the [`useState`](https://react.dev/reference/react/useState) hook - Handle side effects through the [`useEffect`](https://react.dev/reference/react/useEffect) hook ```jsx const name = props.name || "Maria"; const [count, setCount] = useState(1); return (

{count} cheers for {name}!

); ```
In contrast with React, NEAR Components are not wrapped in a `function` or `class` definition. Indeed, when writing a NEAR Component, you focus on writing the body of the component, which is a function that returns the JSX to be rendered. --- ## NEAR Native NEAR Components can readily [interact with smart contracts](./anatomy/near.md) in the NEAR Blockchain. While `view` methods are free to query by anyone, `call` methods require the user to be logged in. ```jsx const counter = Near.view('counter.near-examples.testnet', 'get_num') if(counter === null) return 'Loading...' const add = () => { Near.call('counter.near-examples.testnet', 'increment') } const subtract = () => { Near.call('counter.near-examples.testnet', 'decrement') } return <>

Counter: {counter}

{!context.accountId &&

Please login to interact with the contract

} {context.accountId && <> } ```
--- ## Social from the Get-Go NEAR Components are easily integrated with [NEAR Social](https://near.social/), a social network built on NEAR. ```js const item = (blockHeight) => ({ type: 'social', path: 'influencer.testnet/post/main', blockHeight }); // retrieve indexed posts by influencer.testnet const idx_posts = Social.index( 'post', 'main', { accountId: ['influencer.testnet'] } ); if (idx_posts === null) return 'Loading...'; // retrieve likes for each post const likes = idx_posts.map( index => Social.index('like', item(index.blockHeight)).length ); // retrieve data for each post const post_data = idx_posts.map( index => Social.get(`${index.accountId}/post/main`, index.blockHeight) ); // defined "Like" function const like = (blockHeight) => Social.set( {index:{like: JSON.stringify({key: item(blockHeight), value: {type: 'like'}})}} ) return <>
Posts from influencer.testnet
{post_data.map((post, idx) =>
{JSON.parse(post).text} - {likes[idx]} likes
{context.accountId && }
)} ```
--- ## Fully On-Chain & Easily Composable Leveraging the cheap storage and computation of the NEAR Blockchain, NEAR Components' code is stored fully on-chain in the SocialDB smart contract (`social.near`). ```js // retrieving the code of a stored component return Social.get('influencer.testnet/widget/Greeter') ``` Once deployed, a component can be imported and used by any other component. Composing components as LEGO blocks allows you to build complex applications. ```js // Rendering the component with props return ; ``` --- ## Multi-Chain by Design NEAR Components can easily interact with Ethereum compatible blockchains, helping to easily create decentralized frontends for multi-chain applications. ```js if ( state.chainId === undefined && ethers !== undefined && Ethers.send("eth_requestAccounts", [])[0] ) { Ethers.provider() .getNetwork() .then((chainIdData) => { if (chainIdData?.chainId) { State.update({ chainId: chainIdData.chainId }); } }); } if (state.chainId !== undefined && state.chainId !== 1) { return

Switch to Ethereum Mainnet

; } // FETCH LIDO ABI const lidoContract = "0xae7ab96520de3a18e5e111b5eaab095312d7fe84"; const tokenDecimals = 18; const lidoAbi = fetch( "https://raw.githubusercontent.com/lidofinance/lido-subgraph/master/abis/Lido.json" ); if (!lidoAbi.ok) { return "Loading"; } const iface = new ethers.utils.Interface(lidoAbi.body); // FETCH LIDO STAKING APR if (state.lidoArp === undefined) { const apr = fetch( "https://api.allorigins.win/get?url=https://stake.lido.fi/api/sma-steth-apr" ); if (!apr) return; State.update({ lidoArp: JSON.parse(apr?.body?.contents) ?? "..." }); } // HELPER FUNCTIONS const getStakedBalance = (receiver) => { const encodedData = iface.encodeFunctionData("balanceOf", [receiver]); return Ethers.provider() .call({ to: lidoContract, data: encodedData, }) .then((rawBalance) => { const receiverBalanceHex = iface.decodeFunctionResult( "balanceOf", rawBalance ); return Big(receiverBalanceHex.toString()) .div(Big(10).pow(tokenDecimals)) .toFixed(2) .replace(/\d(?=(\d{3})+\.)/g, "$&,"); }); }; const submitEthers = (strEther, _referral) => { if (!strEther) { return console.log("Amount is missing"); } const erc20 = new ethers.Contract( lidoContract, lidoAbi.body, Ethers.provider().getSigner() ); let amount = ethers.utils.parseUnits(strEther, tokenDecimals).toHexString(); erc20.submit(lidoContract, { value: amount }).then((transactionHash) => { console.log("transactionHash is " + transactionHash); }); }; // DETECT SENDER if (state.sender === undefined) { const accounts = Ethers.send("eth_requestAccounts", []); if (accounts.length) { State.update({ sender: accounts[0] }); console.log("set sender", accounts[0]); } } //if (!state.sender) return "Please login first"; // FETCH SENDER BALANCE if (state.balance === undefined && state.sender) { Ethers.provider() .getBalance(state.sender) .then((balance) => { State.update({ balance: Big(balance).div(Big(10).pow(18)).toFixed(2) }); }); } // FETCH SENDER STETH BALANCE if (state.stakedBalance === undefined && state.sender) { getStakedBalance(state.sender).then((stakedBalance) => { State.update({ stakedBalance }); }); } // FETCH TX COST if (state.txCost === undefined) { const gasEstimate = ethers.BigNumber.from(1875000); const gasPrice = ethers.BigNumber.from(1500000000); const gasCostInWei = gasEstimate.mul(gasPrice); const gasCostInEth = ethers.utils.formatEther(gasCostInWei); let responseGql = fetch( "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: `{ bundle(id: "1" ) { ethPrice } }`, }), } ); if (!responseGql) return ""; const ethPriceInUsd = responseGql.body.data.bundle.ethPrice; const txCost = Number(gasCostInEth) * Number(ethPriceInUsd); State.update({ txCost: `$${txCost.toFixed(2)}` }); } // FETCH CSS const cssFont = fetch( "https://fonts.googleapis.com/css2?family=Manrope:wght@200;300;400;500;600;700;800" ).body; const css = fetch( "https://pluminite.mypinata.cloud/ipfs/Qmboz8aoSvVXLeP5pZbRtNKtDD3kX5D9DEnfMn2ZGSJWtP" ).body; if (!cssFont || !css) return ""; if (!state.theme) { State.update({ theme: styled.div` font-family: Manrope, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; ${cssFont} ${css} `, }); } const Theme = state.theme; // OUTPUT UI const getSender = () => { return !state.sender ? "" : state.sender.substring(0, 6) + "..." + state.sender.substring(state.sender.length - 4, state.sender.length); }; return (
Stake Ether
Stake ETH and receive stETH while staking.
{state.sender && ( <>
Available to stake
{state.balance ?? (!state.sender ? "0" : "...")} ETH
{getSender()}
)}
Staked amount
{state.stakedBalance ?? (!state.sender ? "0" : "...")}  stETH
Lido APR
{state.lidoArp ?? "..."}%
State.update({ strEther: e.target.value })} placeholder="Amount" /> { State.update({ strEther: (state.balance > 0.05 ? parseFloat(state.balance) - 0.05 : 0 ).toFixed(2), }); }} >
{!!state.sender ? ( ) : ( )}
{state.sender && (
You will receive
${state.strEther ?? 0} stETH
)}
Exchange rate
1 ETH = 1 stETH
{false && (
Transaction cost
{state.txCost}
)}
Reward fee
10%
); ``` :::danger ETH Disabled in Docs For a working example visit the [deployed NEAR Component](https://near.social/zavodil.near/widget/Lido). ::: --- ## Next Steps Build and deploy your first components without leaving the browser. Go to https://app.jutsu.ai/, create an account and start building!