Use Tableland with React
Quickly set up a React application with Tableland via an account and database connection.
1. Installation & setup
Create a React app and install Tableland as a dependency.
- JavaScript
- TypeScript
npx create-react-app my-tableland-app
npx create-react-app my-tableland-app --template typescript
Then, cd
into the project and install Tableland.
- npm
- Yarn
- pnpm
npm install --save @tableland/sdk
yarn add @tableland/sdk
pnpm add @tableland/sdk
Under src
, find the App
component, and import Database
from @tableland/sdk
. The SDK already provides access to ethers
—for setting up an account connection, you should also import Signer
and providers
. Lastly, import useState
from react
for a simple way to track the signer in your application's state.
- JavaScript
- TypeScript
import { Database } from "@tableland/sdk";
import { Signer, providers } from "ethers";
import { useState } from "react";
function App() {
return <div></div>;
}
export default App;
import { Database } from "@tableland/sdk";
import { Signer, providers } from "ethers";
import { useState } from "react";
// Provide a global `window` variable to prevent type errors
declare const window: any;
function App() {
return <div></div>;
}
export default App;
2. Connect to a signer
All database creates and writes need a Signer
. Create a connectSigner
method that prompts the browser for a wallet connection, but note there are others ways to create and track an account connection using purpose built web3 libraries (like wagmi). Here, we'll set this up without additional dependencies.
- JavaScript
- TypeScript
import { Database } from "@tableland/sdk";
import { Signer, providers } from "ethers";
import { useState } from "react";
async function connectSigner() {
// Establish a connection with the browser wallet's provider.
const provider = new providers.Web3Provider(window.ethereum);
// Request the connected accounts, prompting a browser wallet popup to connect.
await provider.send("eth_requestAccounts", []);
// Create a signer from the returned provider connection.
const signer = provider.getSigner();
// Return the signer
return signer;
}
function App() {
const [signer, setSigner] = useState();
return <div></div>;
}
export default App;
import { Database } from "@tableland/sdk";
import { Signer, providers } from "ethers";
import { useState } from "react";
declare const window: any;
async function connectSigner(): Promise<Signer> {
// Establish a connection with the browser wallet's provider.
const provider = new providers.Web3Provider(window.ethereum);
// Request the connected accounts, prompting a browser wallet popup to connect.
await provider.send("eth_requestAccounts", []);
// Create a signer from the returned provider connection.
const signer = provider.getSigner();
// Return the signer
return signer;
}
function App() {
const [signer, setSigner] = useState<Signer>();
return <div></div>;
}
export default App;
You'll want create some way of calling this connect method, such as a "connect" wallet button with some click handler.
- JavaScript
- TypeScript
function App() {
const [signer, setSigner] = useState();
async function handleConnect() {
// Connect a signer
const signer = await connectSigner();
setSigner(signer);
}
return (
<div>
<button onClick={async () => handleConnect()}>Connect</button>
</div>
);
}
function App() {
const [signer, setSigner] = useState<Signer>();
async function handleConnect() {
// Connect a signer
const signer = await connectSigner();
setSigner(signer);
}
return (
<div>
<button onClick={async () => handleConnect()}>Connect</button>
</div>
);
}
3. Connect to a Database
Setting up a connection is rather straightforward once you have a signer. Simply create a database instance and pass the signer
as a parameter upon instantiation. Depending on your setup, you might want specific methods that take a signer
as a parameter and pass it to a Database
instantiation.
- JavaScript
- TypeScript
async function connectDatabase(signer) {
// Establish a connection with the database
const db = new Database({ signer });
// Do create, write, and read operations
}
function App() {
const [signer, setSigner] = useState();
async function handleConnect() {
// Connect a signer
const signer = await connectSigner();
setSigner(signer);
// Connect and interact with the database
await connectDatabase(signer);
}
return (
<div>
<button onClick={async () => handleConnect()}>Connect</button>
</div>
);
}
export default App;
async function connectDatabase(signer: Signer) {
// Establish a connection with the database
const db = new Database({ signer });
// Do create, write, and read operations
}
function App() {
const [signer, setSigner] = useState<Signer>();
async function handleConnect() {
// Connect a signer
const signer = await connectSigner();
setSigner(signer);
// Connect and interact with the database
await connectDatabase(signer);
}
return (
<div>
<button onClick={async () => handleConnect()}>Connect</button>
</div>
);
}
export default App;
The example here establishes a connection but doesn't do anything else—you could imagine methods that handle table creations and writing data which could take a signer
, create a database connection, and do some fine tuned operations.
Also, you could track a database singleton using the useState
hook, similar to how the signer
is demonstrated.
- JavaScript
- TypeScript
async function connectDatabase(signer) {
// Establish a connection with the database
const db = new Database({ signer });
// Return the database instance
return db;
}
function App() {
const [signer, setSigner] = useState();
const [database, setDatabase] = useState();
async function handleConnect() {
// Connect a signer
const signer = await connectSigner();
setSigner(signer);
// Connect and interact with the database
const database = await connectDatabase(signer);
setDatabase(database);
}
return (
<div>
<button onClick={async () => handleConnect()}>Connect</button>
</div>
);
}
export default App;
async function connectDatabase(signer: Signer): Promise<Database> {
// Establish a connection with the database
const db = new Database({ signer });
// Return the database instance
return db;
}
function App() {
const [signer, setSigner] = useState<Signer>();
const [database, setDatabase] = useState<Database>();
async function handleConnect() {
// Connect a signer
const signer = await connectSigner();
setSigner(signer);
// Connect and interact with the database
const database = await connectDatabase(signer);
setDatabase(database);
}
return (
<div>
<button onClick={async () => handleConnect()}>Connect</button>
</div>
);
}
export default App;
(Optional) local private key
Alternative to a connectSigner
method that prompts for a browser wallet connection, you can also choose to use a private key stored locally. This might be useful if some database interaction comes from your developer wallet and not the user.
Start by creating a .env
file in your project's root and save your private key here. For React to read an environment variable, you'll need to prefix it with REACT_APP
.
REACT_APP_PRIVATE_KEY=your_private_key
In your component, you can then import the key with process.env.REACT_APP_PRIVATE_KEY
. React should have dotenv
installed (for accessing the variable with process.env
), but if you run into any issues, try installing the package. Thus, if your App
component needs to use a local private key, the connectSigner
method might look a little different.
- JavaScript
- TypeScript
// Keep previous imports, but adjust `ethers`
import { Wallet, getDefaultProvider } from "ethers";
async function connectSigner() {
// Import the private key, available in `.env. using `REACT_APP` prefix.
const privateKey = process.env.REACT_APP_PRIVATE_KEY;
// Define the signer and connect to the provider.
const wallet = new Wallet(privateKey);
// Replace with any provider URL (e.g., Alchemy, Infura, etc.).
const provider = getDefaultProvider("http://127.0.0.1:8545");
const signer = wallet.connect(provider);
// Return the signer
return signer;
}
// Keep previous imports, but adjust `ethers`
import { Wallet, getDefaultProvider } from "ethers";
async function connectSigner() {
// Import the private key, available in `.env. using `REACT_APP` prefix.
const privateKey = process.env.REACT_APP_PRIVATE_KEY;
// Define the signer and connect to the provider.
const wallet = new Wallet(privateKey!);
// Replace with any provider URL (e.g., Alchemy, Infura, etc.).
const provider = getDefaultProvider("http://127.0.0.1:8545");
const signer = wallet.connect(provider);
// Return the signer
return signer;
}