A comprehensive demonstration of how to integrate JustaName into a React application for ENS subname management. This demo showcases the complete workflow of claiming, managing, and revoking ENS subnames with a modern, user-friendly interface.
- Subname Claiming: Create new ENS subnames with real-time availability checking
- Metadata Management: Add and edit text records (email, Twitter, URLs, etc.)
- Address Records: Manage cryptocurrency addresses for different chains
- Content Hash: Set IPFS/Swarm content hashes for decentralized content
- Subname Revocation: Permanently delete subnames when no longer needed
- Real-time Validation: Instant feedback on subname availability
- Debounced Input: Optimized API calls with 500ms debouncing
- Loading States: Clear visual feedback during blockchain operations
- Responsive Design: Works seamlessly on desktop and mobile devices
- Modern UI: Built with shadcn/ui components and Tailwind CSS
- TypeScript: Full type safety throughout the application
- React Hooks: Custom hooks for debouncing and mobile detection
- Wagmi Integration: Ethereum wallet connection and transaction handling
- Error Handling: Comprehensive error handling for blockchain operations
- Framework: React 19 + TypeScript
- Build Tool: Vite
- Styling: Tailwind CSS + shadcn/ui
- Blockchain: Wagmi + JustaName SDK
- State Management: React Query (TanStack Query)
- Form Handling: React Hook Form + Zod validation
-
Clone the repository
git clone https://github.com/your-username/justaname-aurora-integration-demo.git cd justaname-aurora-integration-demo -
Install dependencies
npm install # or yarn install # or bun install
-
Start the development server
npm run dev # or yarn dev # or bun dev
-
Open your browser Navigate to
http://localhost:5173to see the demo in action.
Create a .env file in the root directory:
# Required: JustaName API configuration
VITE_APP_ORIGIN=http://localhost:3000
VITE_APP_DOMAIN=localhost
VITE_APP_MAINNET_PROVIDER_URL=https://eth.drpc.org
VITE_APP_MAINNET_ENS_DOMAIN=ens_domain
VITE_APP_MAINNET_API_KEY=your_justaname_api_key_hereThe demo uses the JustaName React SDK which is configured in src/providers.tsx. Key configuration points:
- Network: Mainnet Ethereum (chainId: 1)
- ENS Domain:
.eth(configurable) - Wallet Connection: Supports all major wallets via Wagmi
- JustANameProvider: Wraps the app to provide JustaName functionality
import { useAddSubname } from "@justaname.id/react";
function MyComponent() {
const { addSubname, isAddSubnamePending } = useAddSubname();
const handleClaim = () => {
addSubname({
username: "alice",
text: {
email: "alice@example.com",
twitter: "@alice",
},
addresses: {
2147492101: address,
// Note: For EVM chains other than Ethereum, coinType = chainId + 2147483648
// Ethereum uses coinType 60
// Example: Base (chainId 8453) = 8453 + 2147483648 = 2147492101
},
});
};
return (
<button onClick={handleClaim} disabled={isAddSubnamePending}>
{isAddSubnamePending ? "Claiming..." : "Claim Subname"}
</button>
);
}import { useIsSubnameAvailable } from "@justaname.id/react";
function AvailabilityChecker() {
const { isSubnameAvailable, isSubnameAvailablePending } =
useIsSubnameAvailable({
username: "alice",
ensDomain: "example.eth",
});
if (isSubnameAvailablePending) return <div>Checking...</div>;
return (
<div>
{isSubnameAvailable?.isAvailable ? "Available!" : "Already taken"}
</div>
);
}import { useAccountSubnames } from "@justaname.id/react";
function SubnamesList() {
const { accountSubnames } = useAccountSubnames();
if (accountSubnames.length === 0) {
return <div>No subnames found</div>;
}
return (
<div>
<h3>Your Subnames ({accountSubnames.length})</h3>
{accountSubnames.map((subname, index) => (
<div key={index}>
<div>{subname.ens}</div>
{/* Display record counts */}
{subname.records?.texts && (
<span>
{Object.keys(subname.records.texts).length} text records
</span>
)}
{subname.records?.coins && (
<span>{Object.keys(subname.records.coins).length} addresses</span>
)}
</div>
))}
</div>
);
}import { useRecords } from "@justaname.id/react";
import { useState } from "react";
function ENSResolver() {
const [ensName, setEnsName] = useState("");
const { records, isRecordsFetching } = useRecords({
ens: ensName,
chainId: 1,
});
return (
<div>
<input
type="text"
placeholder="Enter ENS name (e.g., vitalik.eth)"
value={ensName}
onChange={(e) => setEnsName(e.target.value)}
/>
{isRecordsFetching && <p>Resolving...</p>}
{records && (
<div>
{/* Text Records */}
{records.records.texts &&
Object.entries(records.records.texts).map(([, value]) => (
<div key={value.key}>
<strong>{value.key}:</strong> {value.value}
</div>
))}
{/* Address Records */}
{records.records.coins &&
Object.entries(records.records.coins).map(([coinType, address]) => (
<div key={coinType}>
<strong>
{coinType === "60" ? "ETH" : `Coin ${coinType}`}:
</strong>{" "}
{address.value}
</div>
))}
</div>
)}
</div>
);
}import { useUpdateSubname } from "@justaname.id/react";
function RecordManager() {
const { updateSubname, isUpdateSubnamePending } = useUpdateSubname();
const updateRecords = () => {
updateSubname({
ens: "alice.example.eth",
chainId: 1,
text: {
email: "newemail@example.com",
website: "https://alice.com",
},
addresses: {
"60": "0x1234...", // ETH address
"0": "bc1...", // BTC address
},
contentHash: "ipfs://QmYourHashHere",
});
};
return (
<button onClick={updateRecords} disabled={isUpdateSubnamePending}>
Update Records
</button>
);
}src/
βββ components/
β βββ subname/
β β βββ SubnameForm.tsx # Subname creation form
β β βββ SubnamesList.tsx # List of user's subnames
β β βββ SubnameDisplay.tsx # Individual subname management
β βββ wallet/
β β βββ ConnectWallet.tsx # Wallet connection
β β βββ Account.tsx # Account display
β βββ ui/ # shadcn/ui components
βββ config/
β βββ constants.ts # App constants
βββ hooks/
β βββ use-debounce.ts # Custom debounce hook
β βββ use-mobile.ts # Mobile detection hook
βββ wagmi/
β βββ config.ts # Wagmi configuration
βββ App.tsx # Main application component
- Purpose: Create new ENS subnames
- Features: Real-time availability checking, metadata configuration
- Validation: Ensures subname availability and proper metadata format
- Purpose: Display all user's subnames
- Features: Record count display, status indicators
- Behavior: Only renders when user has subnames
- Purpose: Manage individual subname records
- Features: Text records, address records, content hash management
- Actions: Update records, revoke subname
- Purpose: Provides JustaName functionality to the entire app
- Configuration: Handles API keys, network settings, and ENS domain configuration
- Required: Must wrap your app for all JustaName hooks to work
bun install @justaname.id/react wagmi viem @tanstack/react-query// src/wagmi/config.ts
import { createConfig, http } from "wagmi";
import { mainnet } from "wagmi/chains";
import { injected, walletConnect } from "wagmi/connectors";
export const config = createConfig({
chains: [mainnet],
connectors: [injected(), walletConnect()],
transports: {
[mainnet.id]: http(),
},
});// src/providers.tsx
import { WagmiProvider } from "wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { JustaNameProvider } from "@justaname.id/react";
import { config } from "./wagmi/config";
const queryClient = new QueryClient();
const justanameConfig = {
config: {
origin: "http://localhost:3000",
domain: "localhost",
},
networks: [
{
chainId: 1,
providerUrl: "https://eth.drpc.org",
},
],
ensDomains: [
{
ensDomain: "ens-domain",
chainId: 1,
apiKey: "your_justaname_api_key_here",
},
],
};
export function Providers({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<JustaNameProvider config={justanameConfig}>
{children}
</JustaNameProvider>
</QueryClientProvider>
</WagmiProvider>
);
}import { useAddSubname, useAccountSubnames } from "@justaname.id/react";
function MyApp() {
const { accountSubnames } = useAccountSubnames();
return (
<div>
<h1>My Subnames: {accountSubnames.length}</h1>
{/* Your components here */}
</div>
);
}Important: The JustaNameProvider must wrap your app for all hooks to work properly.
- Private Keys: Never expose private keys in client-side code
- Transaction Signing: All transactions require user wallet approval
- Network Selection: Ensure users are on the correct network (Mainnet)
- JustANameProvider: Always wrap your app with JustANameProvider for hooks to work
- Debouncing: Always debounce user input for availability checks
- Loading States: Provide clear feedback during blockchain operations
- Error Handling: Implement comprehensive error handling
- Validation: Validate all user inputs before submission
- API Keys: Keep your JustaName API key secure and leverage the domain allow list to secure it
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have questions:
- Check the JustaName documentation
- Review the ENS documentation
- Open an issue in this repository
Built with β€οΈ using JustaName, React, Wagmi, and modern web technologies.