Building a Basic Healthcare Records DApp on Stacks

Table of contents
- Solving medical record chaos with blockchain — one patient at a time — A Real-World Patient Story
- Introduction
- Problem-Solution Thinking
- Tech Stack
- Step 1: Smart Contract Logic — PatientRecord.clar
- Step 2: Connecting to the Smart Contract — contract.tsx
- Step 3: Connecting Wallet — ConnectWallet.tsx
- Step 4: Frontend Flow — Dashboard.tsx and Home.tsx
- Lessons Learned
- Final Thoughts
- Thanks for reading — and don’t forget to get your health data encrypted! 🧬🔐

Solving medical record chaos with blockchain — one patient at a time — A Real-World Patient Story
Introduction
I’ve always wanted a way for patients to avoid retaking the same medical tests just because they’ve moved or switched hospitals. What if I lost my health records notebook? What if I couldn’t recall my treatment history during an emergency?
That’s been my story — moving around, visiting hospitals repeatedly, and facing the same diagnosis process each time simply because my data wasn’t accessible. So I imagined a world where my health record could follow me, securely, digitally, and with my control over who sees it and when.
That vision led to HealthEncrypted — a secure, decentralized application built on the Stacks blockchain, using Clarity smart contracts and a modern React + TypeScript + Vite frontend.
Let me show you how it can be built, what I learned, and how you can try doing the same.
Problem-Solution Thinking
Problem: Patients can’t easily access their health records across multiple providers.
Solution: Build a blockchain-powered DApp that allows patients to:
Register and store encrypted health data
Give hospitals permissioned access
Update and retrieve records securely
Tech Stack
Smart Contract: Clarity on Stacks
Frontend: React + TypeScript + Vite
Wallet: Hiro Wallet for authentication
Step 1: Smart Contract Logic — PatientRecord.clar
This is where the data structure and access control begin. We define a map called patient-records
to track core patient data
(define-map patients ((id (string-utf8 30)))
((name (string-utf8 50)) (dob uint) (blood-type (string-utf8 5))))
We also track health records, but rather than store full data on-chain (privacy risk!), we store an encrypted hash or reference:
(define-map records ((id (string-utf8 30))) ((record-hash (string-utf8 100))))
Here's how patients are registered:
(define-public (register-patient (id (string-utf8 30)) (name (string-utf8 50)) (dob uint) (blood-type (string-utf8 5)))
(begin
(map-set patients ((id id)) ((name name) (dob dob) (blood-type blood-type)))
(ok true)))
...and updated:
(define-public (update-patient-record (id (string-utf8 30)) (record-hash (string-utf8 100)))
(begin
(map-set records ((id id)) ((record-hash record-hash)))
(ok true)))
What I learned:
String limits are strict. Plan your field lengths carefully.
Map access is explicit. No shortcuts; Clarity makes you think through every data call.
Step 2: Connecting to the Smart Contract — contract.tsx
In secure-health-frontend/src/utils/contract.tsx
, I created async functions to interact with Clarity:
export async function registerPatient(...) => {
contractAddress: 'ST1...'
functionName: 'register-patient',
functionArgs: [ stringUtf8CV(id), stringUtf8CV(name), uintCV(dob), stringUtf8CV(bloodType) ]
}
Same for fetching and updating records. Each function builds a contract call payload that can be used by @stacks/connect
to trigger wallet interaction.
What I learned:
It’s best to keep all contract logic in one utility file.
Always handle errors from wallet or network issues.
Step 3: Connecting Wallet — ConnectWallet.tsx
Authentication is handled using Hiro Wallet, managed in AuthContext
and triggered via:
<button onClick={handleConnect}>Connect Wallet</button>
Once authenticated, users can access their dashboard securely.
Step 4: Frontend Flow — Dashboard.tsx
and Home.tsx
Home.tsx
serves as the landing page. If the user is authenticated, they’re redirected to the dashboard:
{isAuthenticated ? 'Go to Dashboard' : 'Get Started'}
The Dashboard.tsx
component fetches patient records and renders them beautifully:
useEffect(() => {
if (!isAuthenticated) return;
const fetchRecords = async () => {
const response = await getPatientRecord('demo-patient-id');
const result = await openContractCall(response); // using @stacks/connect
setRecords(result); // assuming a transformation step
};
fetchRecords();
}, [isAuthenticated]);
This interaction leverages the getPatientRecord()
function from contract.tsx
, which builds the contract call like this:
export async function getPatientRecord(patientId: string) {
const options: ContractCallOptions = {
contractAddress: CONTRACT_ADDRESS,
contractName: CONTRACT_NAME,
functionName: 'get-patient-record',
functionArgs: [stringUtf8CV(patientId)],
network,
};
return options;
}
What I learned:
Designing a pleasant UI matters. I used TailwindCSS to keep things elegant.
State and auth flows must be tightly scoped to avoid leaking patient context.
Abstracting contract logic into
contract.tsx
made things more maintainable and testable.
Home.tsx
serves as the landing page. If the user is authenticated, they’re redirected to the dashboard:
{isAuthenticated ? 'Go to Dashboard' : 'Get Started'}
The Dashboard.tsx
component fetches patient records and renders them beautifully:
useEffect(() => {
// placeholder — mock fetch, to be replaced with real contract call
setRecords([...])
}, [])
What I learned:
Designing a pleasant UI matters. I used TailwindCSS to keep things elegant.
State and auth flows must be tightly scoped to avoid leaking patient context.
Lessons Learned
Start with the pain point. My idea came from real-world frustration, not code.
Build first, polish later. I mocked records before wiring up the contract fully.
Separate logic and UI. Contracts, auth, and UI are all in their own layers.
Final Thoughts
HealthEncrypted is far from complete. But the bones are strong. With secure on-chain references, wallet-authenticated access, and clean separation of logic, the DApp can scale to more complex health data workflows.
If you’ve ever thought, "Why can’t my records just follow me?" — this tutorial shows you how to begin answering that question using blockchain.
You can view the full code on GitHub.
Feel free to fork it, remix it, or contribute.
If you're working on healthcare, data privacy, or even something completely different but on Stacks — let's connect. I’d love to hear how others are solving real-world problems with Web3 tech.
Thanks for reading — and don’t forget to get your health data encrypted! 🧬🔐
Subscribe to my newsletter
Read articles from Gbolahan Akande directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
