Skip to main content
The ScramjetFrame class provides an abstraction over proxy iframe creation, allowing you to manage isolated browsing contexts without worrying about proxy internals.

Creating a frame

Frames are created using the createFrame() method on your ScramjetController instance:
const { ScramjetController } = $scramjetLoadController();
const scramjet = new ScramjetController({ prefix: '/scramjet/' });
await scramjet.init();

// Create a new frame
const frame = scramjet.createFrame();

// Add it to the DOM
document.body.appendChild(frame.frame);
You can also pass an existing iframe element to createFrame() if you want to use a pre-configured iframe:
const existingIframe = document.getElementById('myIframe');
const frame = scramjet.createFrame(existingIframe);
The ScramjetFrame class provides several methods for controlling navigation within the isolated context: Use the go() method to navigate to a new URL:
// Navigate with a string URL
frame.go('https://example.com');

// Navigate with a URL object
frame.go(new URL('https://example.com/page'));

Browser history navigation

// Go back in history
frame.back();

Accessing frame properties

The ScramjetFrame instance provides access to useful properties:

Current URL

Get the current proxified URL:
const currentUrl = frame.url;
console.log('Current URL:', currentUrl.href);
console.log('Hostname:', currentUrl.hostname);
console.log('Path:', currentUrl.pathname);

ScramjetClient instance

Access the ScramjetClient instance running inside the iframe:
const client = frame.client;
// The client provides low-level access to the proxy internals
The client property gives you access to the Scramjet instance running within the iframe’s context, allowing for advanced manipulation if needed.

Raw iframe element

Access the underlying HTMLIFrameElement:
const iframeElement = frame.frame;

// You can manipulate the iframe directly
iframeElement.style.width = '100%';
iframeElement.style.height = '600px';
iframeElement.setAttribute('sandbox', 'allow-scripts allow-same-origin');

Building a browser interface

Here’s a complete example of building a simple browser interface with navigation controls:
<!DOCTYPE html>
<html>
<head>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: system-ui, -apple-system, sans-serif;
      display: flex;
      flex-direction: column;
      height: 100vh;
    }

    .toolbar {
      display: flex;
      gap: 8px;
      padding: 12px;
      background: #f5f5f5;
      border-bottom: 1px solid #ddd;
    }

    .toolbar button {
      padding: 8px 16px;
      border: 1px solid #ccc;
      background: white;
      cursor: pointer;
      border-radius: 4px;
    }

    .toolbar button:hover {
      background: #e9e9e9;
    }

    .toolbar input {
      flex: 1;
      padding: 8px 12px;
      border: 1px solid #ccc;
      border-radius: 4px;
      font-size: 14px;
    }

    #frameContainer {
      flex: 1;
      position: relative;
    }

    iframe {
      width: 100%;
      height: 100%;
      border: none;
    }
  </style>
</head>
<body>
  <div class="toolbar">
    <button id="backBtn">← Back</button>
    <button id="forwardBtn">Forward →</button>
    <button id="reloadBtn">↻ Reload</button>
    <input type="text" id="urlBar" placeholder="Enter URL..." />
    <button id="goBtn">Go</button>
  </div>
  <div id="frameContainer"></div>

  <script>
    (async () => {
      // Initialize Scramjet
      if ('serviceWorker' in navigator) {
        await navigator.serviceWorker.register('/scramjet.all.js', {
          scope: '/scramjet/',
        });
      }

      await navigator.serviceWorker.ready;

      const { ScramjetController } = $scramjetLoadController();
      const scramjet = new ScramjetController({
        prefix: '/scramjet/',
      });
      await scramjet.init();

      // Create frame
      const frame = scramjet.createFrame();
      document.getElementById('frameContainer').appendChild(frame.frame);

      // Get UI elements
      const urlBar = document.getElementById('urlBar');
      const backBtn = document.getElementById('backBtn');
      const forwardBtn = document.getElementById('forwardBtn');
      const reloadBtn = document.getElementById('reloadBtn');
      const goBtn = document.getElementById('goBtn');

      // Navigation functions
      const navigate = () => {
        let url = urlBar.value.trim();
        if (!url) return;

        // Add protocol if missing
        if (!url.match(/^https?:\/\//)) {
          url = 'https://' + url;
        }

        frame.go(url);
      };

      // Event listeners
      goBtn.addEventListener('click', navigate);
      urlBar.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') navigate();
      });

      backBtn.addEventListener('click', () => frame.back());
      forwardBtn.addEventListener('click', () => frame.forward());
      reloadBtn.addEventListener('click', () => frame.reload());

      // Update URL bar when navigation occurs
      frame.addEventListener('urlchange', (event) => {
        urlBar.value = event.url;
        document.title = event.url;
      });

      // Optional: Log navigation events
      frame.addEventListener('navigate', (event) => {
        console.log('Navigating to:', event.url);
      });

      // Start with a default page
      frame.go('https://example.com');
    })();
  </script>
</body>
</html>

Multiple frames

You can create and manage multiple frames simultaneously:
const { ScramjetController } = $scramjetLoadController();
const scramjet = new ScramjetController({ prefix: '/scramjet/' });
await scramjet.init();

// Create multiple frames
const frame1 = scramjet.createFrame();
const frame2 = scramjet.createFrame();

document.getElementById('container1').appendChild(frame1.frame);
document.getElementById('container2').appendChild(frame2.frame);

// Navigate each frame independently
frame1.go('https://example.com');
frame2.go('https://example.org');

// Each frame has independent event listeners
frame1.addEventListener('urlchange', (e) => {
  console.log('Frame 1 URL:', e.url);
});

frame2.addEventListener('urlchange', (e) => {
  console.log('Frame 2 URL:', e.url);
});

Advanced usage

Detecting when context is ready

Listen for the contextInit event to know when the frame has fully initialized:
frame.addEventListener('contextInit', (event) => {
  console.log('Frame initialized!');
  console.log('Window object:', event.window);
  console.log('Client instance:', event.client);
});

Styling frames

Style the iframe element to fit your application:
const frame = scramjet.createFrame();

// Apply styles directly
frame.frame.style.cssText = `
  width: 100%;
  height: 100%;
  border: none;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
`;

// Or use classes
frame.frame.className = 'proxy-frame';

Frame cleanup

When you’re done with a frame, remove it from the DOM:
// Remove the frame
frame.frame.remove();

// Or keep a reference and reuse it later
const frames = new Map();
frames.set('main', frame);

// Later...
const mainFrame = frames.get('main');
mainFrame.go('https://newurl.com');

Event handling

Learn how to handle navigation, URL changes, and download events

Cookie management

Manage cookies within isolated frame contexts

Build docs developers (and LLMs) love