-
Notifications
You must be signed in to change notification settings - Fork 1
Pi Browser Detection
alxspiker edited this page Jul 31, 2025
·
2 revisions
Build hybrid applications that work seamlessly in both Web2 and Web3 environments
The essential guide for creating Pi Network apps that provide great experiences for all users
The essential guide for creating Pi Network apps that provide great experiences for all users
π The future of Pi Network development is hybrid apps
Modern Pi Network applications embrace a hybrid architecture that delivers:
- π Universal Accessibility - Works in Chrome, Safari, Firefox, and all browsers
- β‘ Enhanced Pi Features - Full Pi SDK integration in Pi Browser
- π‘οΈ Graceful Degradation - Smart fallbacks for non-Pi users
- π± Cross-Platform Reach - Desktop, mobile, and Pi Browser compatibility
π― What This Guide Covers:
- Smart Pi Browser detection methods
- Hybrid app architecture patterns
- Production-ready implementation examples
- Best practices for user experience
π‘ The Problem: Without proper detection, Pi SDK calls in regular browsers cause confusing popups and broken functionality
β Without Detection
- Scary authentication popups
- JavaScript errors & crashes
- Poor user experience
- Limited app reach
β
With Smart Detection
- Targeted Pi features only when available
- Clean UI for all browser users
- Professional fallback experiences
- Maximum app compatibility
| π― Benefit | π Description |
|---|---|
| π‘οΈ Graceful Fallbacks | Show helpful messaging instead of broken functionality |
| β¨ Better UX | No confusing popups or authentication errors |
| π Broader Reach | Your app works for everyone, enhanced for Pi users |
| π Future-Ready | Prepared for Pi Network's mainstream adoption |
Create a custom hook for easy Pi Browser detection in React applications:
// hooks/useIsPiBrowser.js
import { useEffect, useState } from "react";
const useIsPiBrowser = () => {
const [isInPiBrowser, setIsInPiBrowser] = useState(null);
useEffect(() => {
let cancelled = false;
const detect = async () => {
// Set timeout for detection
const timeout = new Promise((resolve) =>
setTimeout(() => resolve("timeout"), 3000)
);
try {
// Primary detection method
if (window?.Pi?.getPiHostAppInfo) {
const result = await Promise.race([
window.Pi.getPiHostAppInfo(),
timeout,
]);
if (!cancelled && result?.hostApp === "pi-browser") {
setIsInPiBrowser(true);
return;
}
}
} catch (e) {
console.warn("β Pi SDK detection failed", e);
}
// Fallback detection methods
const ua = navigator?.userAgent?.toLowerCase() || "";
const isLikelyPi =
ua.includes("pibrowser") ||
ua.includes("pi-browser") ||
document.referrer.includes("minepi.com") ||
document.referrer.includes("pi.app");
if (!cancelled) {
setIsInPiBrowser(isLikelyPi);
}
};
detect();
return () => {
cancelled = true;
};
}, []);
return isInPiBrowser; // Returns: true | false | null (detecting)
};
export default useIsPiBrowser;For non-React applications or when you need a framework-agnostic solution:
// Vanilla JS Pi Browser Detection
async function detectPiBrowser() {
const timeout = new Promise((resolve) =>
setTimeout(() => resolve("timeout"), 3000)
);
try {
// Primary method: Use Pi SDK's getPiHostAppInfo
if (window?.Pi?.getPiHostAppInfo) {
const result = await Promise.race([
window.Pi.getPiHostAppInfo(),
timeout,
]);
if (result?.hostApp === "pi-browser") {
return true;
}
}
} catch (e) {
console.warn("β Pi SDK detection failed", e);
}
// Fallback detection methods
const ua = navigator?.userAgent?.toLowerCase() || "";
const referrer = document.referrer.toLowerCase();
return ua.includes("pibrowser") ||
ua.includes("pi-browser") ||
referrer.includes("minepi.com") ||
referrer.includes("pi.app") ||
window.location.hostname.includes("pi.app");
}import useIsPiBrowser from "../hooks/useIsPiBrowser";
const LoginComponent = () => {
const isInPi = useIsPiBrowser();
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleLogin = async () => {
if (!isInPi) {
alert('Please open this app in Pi Browser to login');
return;
}
try {
const auth = await Pi.authenticate(['payments'], onIncompletePaymentFound);
setIsLoggedIn(true);
console.log('Authentication successful:', auth);
} catch (error) {
console.error('Authentication failed:', error);
}
};
// Still detecting browser type
if (isInPi === null) {
return <div className="loading">π Detecting Pi Browser...</div>;
}
// Not in Pi Browser - show fallback
if (!isInPi) {
return (
<div className="fallback-ui">
<h3>π₯§ Pi Features Available in Pi Browser</h3>
<p>To access Pi authentication and payments:</p>
<a href="https://pinet.com/YOUR_APP_LINK" className="pi-button">
π± Open in Pi Browser
</a>
</div>
);
}
// In Pi Browser - show full functionality
return (
<div className="pi-features">
<button onClick={handleLogin} className="login-btn">
{isLoggedIn ? 'β
Logged In' : 'π Login with Pi'}
</button>
</div>
);
};
const PaymentButton = () => {
const isInPi = useIsPiBrowser();
if (!isInPi) {
return (
<div className="payment-fallback">
<h4>π° Payments available in Pi Browser</h4>
<img src="/qr-code.png" alt="Scan with Pi Browser" />
<p>Scan this QR code with Pi Browser to make payments</p>
</div>
);
}
return (
<button onClick={handlePayment} className="payment-btn">
π Pay with Pi
</button>
);
};// Initialize app after page load
document.addEventListener('DOMContentLoaded', async () => {
const isPiBrowser = await detectPiBrowser();
if (!isPiBrowser) {
// Show fallback UI for regular browsers
document.getElementById('app').innerHTML = `
<div class="fallback-container">
<h2>π Welcome to Our Pi Network App!</h2>
<p>This app integrates with Pi Network for secure payments and authentication.</p>
<div class="feature-preview">
<h3>β¨ Available Features:</h3>
<ul>
<li>π Secure Pi Network authentication</li>
<li>π° Pi cryptocurrency payments</li>
<li>π Exclusive Pi Network rewards</li>
</ul>
</div>
<div class="cta-section">
<p><strong>To access all features:</strong></p>
<a href="https://pinet.com/YOUR_APP_LINK" class="big-cta-button">
π± Open in Pi Browser
</a>
</div>
<div class="help-section">
<p>Don't have Pi Browser?
<a href="https://play.google.com/store/apps/details?id=pi.browser">
Download it here
</a>
</p>
</div>
</div>
`;
return;
}
// Pi Browser detected - enable full Pi features
enablePiFeatures();
});
function enablePiFeatures() {
// Setup login functionality
document.getElementById('login-btn')?.addEventListener('click', async () => {
try {
const auth = await Pi.authenticate(['payments'], onIncompletePaymentFound);
console.log('Authentication successful:', auth);
updateUIForLoggedInUser(auth);
} catch (error) {
console.error('Authentication failed:', error);
}
});
// Setup payment functionality
document.getElementById('pay-btn')?.addEventListener('click', handlePayment);
}// DON'T: Auto-authenticate on page load
window.onload = () => {
Pi.authenticate(['payments'], callback); // Scary popup in regular browsers!
};
// DON'T: Call Pi SDK methods without detection
Pi.createPayment(paymentData); // Will fail in regular browsers
// DON'T: Show confusing error messages
if (!window.Pi) {
alert("Pi SDK not found!"); // Confusing for regular users
}// DO: Detect first, then act
const isPiBrowser = await detectPiBrowser();
if (isPiBrowser) {
// Only call Pi SDK methods in Pi Browser
Pi.authenticate(['payments'], callback);
}
// DO: Provide helpful fallbacks
if (!isPiBrowser) {
showPiBrowserPrompt(); // Clear guidance for users
}
// DO: Use user-initiated actions
button.onclick = async () => {
if (await detectPiBrowser()) {
Pi.authenticate(['payments'], callback);
} else {
showPiBrowserRequired();
}
};- Don't authenticate on page load β Wait for user interaction
-
Wrap Pi SDK features conditionally β Only show when
isInPiBrowser === true - Provide clear fallback UI β QR codes, download links, helpful messages
- Use graceful degradation β App should work without Pi features
- Test in both environments β Regular browsers AND Pi Browser
-
Handle the
nullstate β Show loading while detecting - Cache detection results β Avoid repeated detection calls
const App = () => {
const isInPi = useIsPiBrowser();
return (
<div>
{/* Core functionality for everyone */}
<MainContent />
{/* Enhanced features for Pi Browser users */}
{isInPi && <PiFeatures />}
{/* Helpful guidance for regular browser users */}
{isInPi === false && <PiBrowserPrompt />}
{/* Loading state while detecting */}
{isInPi === null && <DetectionLoader />}
</div>
);
};const FeatureCard = ({ title, description, requiresPi = false }) => {
const isInPi = useIsPiBrowser();
if (requiresPi && !isInPi) {
return (
<div className="feature-card disabled">
<h3>{title} π</h3>
<p>{description}</p>
<div className="pi-required">
<small>π₯§ Available in Pi Browser</small>
</div>
</div>
);
}
return (
<div className="feature-card">
<h3>{title}</h3>
<p>{description}</p>
{requiresPi && <span className="pi-badge">π₯§ Pi Feature</span>}
</div>
);
};Hybrid apps are the bridge to Pi's mainstream adoption. Smart SDK detection avoids bad UX and keeps your app clean.
Head to pi-sdk-starter β a complete template with essential features like Donate, Connect Wallet, and Ad Rewards already wired up. Plug it in, customize, and ship faster.
- SDK Reference - Complete Pi SDK documentation
- Authorization - User authentication patterns
- Payments Overview and Workflow - Payment integration guide
- π Home - Complete developer handbook
- π Community Support - Get help from Pi developers
- π Authorization - Authentication & security patterns
- π API Reference - Complete REST API documentation
- β‘ SDK Reference - JavaScript SDK comprehensive guide
- π Data Types - Object structures & schemas
- π Pi Browser Detection - Build hybrid Web2/Web3 apps
- π Migration Guide - Upgrade to hybrid architecture
- π» Code Examples - Production-ready samples & templates
- π What is PiNet? - Cross-platform app accessibility
- π³ What is PiWallet? - Pi Network wallet integration
- π° Payments Overview - Transaction handling guide
- π¬ Chat Rooms for Apps - Community engagement features
- π Mainnet vs. Testnet - Environment selection guide
- π Developer Terms - Terms of service
- βοΈ Social Chain ToS - Platform terms
- π Whitepaper - Pi Network foundations
π‘ Need help? Join our Discord community!