Skip to content

Pi Browser Detection

alxspiker edited this page Jul 31, 2025 · 2 revisions

🌐 Pi Browser Detection Guide

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

🎯 Overview

πŸš€ 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

βœ… Why This Matters

πŸ’‘ 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

🌟 Key Benefits

🎯 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

πŸ”§ Implementation Methods

Method 1: React Hook (Recommended for React Apps)

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;

Method 2: Vanilla JavaScript (Universal Solution)

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");
}

πŸ’» Usage Examples

React Component Example

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>
  );
};

Vanilla JavaScript Example

// 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);
}

⚑ Best Practices

❌ What NOT to do

// 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
}

βœ… What TO do

// 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();
  }
};

🎯 Implementation Tips

  1. Don't authenticate on page load β€” Wait for user interaction
  2. Wrap Pi SDK features conditionally β€” Only show when isInPiBrowser === true
  3. Provide clear fallback UI β€” QR codes, download links, helpful messages
  4. Use graceful degradation β€” App should work without Pi features
  5. Test in both environments β€” Regular browsers AND Pi Browser
  6. Handle the null state β€” Show loading while detecting
  7. Cache detection results β€” Avoid repeated detection calls

🎨 UI/UX Patterns

Progressive Enhancement Pattern

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>
  );
};

Conditional Feature Rendering

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>
  );
};

Final Words

Hybrid apps are the bridge to Pi's mainstream adoption. Smart SDK detection avoids bad UX and keeps your app clean.

Need a jumpstart?

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.

See Also

🧭 Pi Developer Navigation

πŸš€ Getting Started


πŸ“– Core References


πŸ› οΈ Implementation Guides


🌟 Platform Features


βš™οΈ Environment & Deployment


πŸ“œ Legal & Compliance


πŸ“‘ Resources & Whitepapers


πŸ’‘ Need help? Join our Discord community!

Clone this wiki locally