# Websites

**URL:** <https://hotbunny.dev/t/websites/34>\
**Category:** Guides\
**Created:** [October 21, 2026, 3:58pm UTC](https://hotbunny.dev/t/websites/34 "2026-10-21T15:58:18Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![SteelWaffe7](https://hotbunny.dev/user_avatar/hotbunny.dev/steelwaffe7/32/11_2.png) [@SteelWaffe7](https://hotbunny.dev/u/SteelWaffe7)\
**Post date:** [October 21, 2026, 3:58pm UTC](https://hotbunny.dev/t/websites/34/1 "2026-10-21T15:58:18Z")

</div>

Create in-game websites accessible through the Chromo browser. Websites use HTML pages rendered inside sandboxed WebViews.

## Basic Website

```typescript
import { Website, RegisterWebsite } from "@hotbunny/hackhub-content-sdk";
import homePage from "./pages/home.html";
import aboutPage from "./pages/about.html";
import postsPage from "./pages/posts.html";

@RegisterWebsite
export class HackerForum extends Website {

    SiteName = "Hacker Forum";
    Host = "hackerforum.net";
    Icon = "./assets/forum-icon.png";
    Pages = [
        { path: "/", title: "Home", html: homePage },
        { path: "/about", title: "About", html: aboutPage },
        { path: "/posts", title: "Posts", html: postsPage },
    ];

}

```

Import HTML files directly - the build system bundles them as strings automatically.

## Properties

| Property | Type | Description |
| --- | --- | --- |
| `SiteName` | `string` | Display name of the website |
| `Host` | `string` | Domain name (e.g. `hackerforum.net`) |
| `Icon` | `string` | Path to the website favicon |
| `Pages` | `(WebsitePageDefinition | DynamicWebsitePageDefinition)[]` | Array of page definitions |
| `Popular` | `boolean` | Show in the browser’s popular sites |
| `Exports` | `Record<string, any>` | Functions/values exposed to HTML |

## Pages

Each page in the `Pages` array has:

| Property | Type | Description |
| --- | --- | --- |
| `path` | `string` | URL path (e.g. `/`, `/about`, `/products/:id`) |
| `title` | `string` | Page title shown in the browser tab |
| `html` | `string` | HTML content (import from `.html` file) |
| `seo` | `boolean?` | If true, page appears in browser search results |
| `description` | `string?` | Description shown in search results |
| `search` | `string[]?` | Extra keywords for search ranking |

Example with description and search keywords:

```typescript
Pages = [
    {
        path: "/",
        title: "Hacker Forum - Home",
        description: "The underground community for elite hackers.",
        html: homePage,
        seo: true,
        search: ["hacking", "forum", "underground"],
    },
    {
        path: "/about",
        title: "About Us",
        description: "Learn more about Hacker Forum.",
        html: aboutPage,
        seo: true,
    },
];

```

## Dynamic Pages

For pages with URL parameters (e.g. `/products/:id`), use `DynamicWebsitePageDefinition` with a `metadata()` function instead of static `title`/`html`:

```typescript
import { Website, RegisterWebsite, DynamicWebsitePageDefinition } from "@hotbunny/hackhub-content-sdk";
import productPage from "./pages/product.html";
import homePage from "./pages/home.html";

@RegisterWebsite
export class MyShop extends Website {

    SiteName = "My Shop";
    Host = "myshop.mod";
    Icon = "";

    Pages = [
        { path: "/", title: "My Shop", html: homePage, seo: true },
        {
            path: "/products/:productId",
            seo: true,
            metadata(context) {
                const products: Record<string, { name: string; desc: string }> = {
                    "1": { name: "VPN Pro", desc: "Anonymous browsing tool" },
                    "2": { name: "CrackKit", desc: "Password recovery suite" },
                };
                const product = products[context.params.productId];
                if (!product) return null; // 404

                return {
                    title: `${product.name} - My Shop`,
                    description: product.desc,
                    html: productPage,
                    search: [product.name.toLowerCase()],
                    exports: { product },
                };
            },
        } satisfies DynamicWebsitePageDefinition,
    ];
}

```

### PageContext

The `metadata()` function receives a context object:

| Property | Type | Description |
| --- | --- | --- |
| `url` | `string` | Full URL being navigated to |
| `params` | `Record<string, string>` | Route parameters (`:productId` → `"1"`) |
| `query` | `Record<string, string>` | Query string parameters (`?sort=price`) |
| `searchStr` | `string?` | Search query (when called from Goagle search) |

### PageMetadata

Return an object describing the page, or `null` to show a 404:

| Property | Type | Description |
| --- | --- | --- |
| `title` | `string` | Page title |
| `description` | `string?` | Search result description |
| `html` | `string` | HTML content to render |
| `search` | `string[]?` | Dynamic search keywords |
| `exports` | `Record<string, any>?` | Values exposed as globals in the HTML |

## HTML Pages

Your HTML files are rendered inside sandboxed WebViews. You have full control over the page content:

```html
<!DOCTYPE html>
<html>
<head>
    <style>
        body { background: #1a1a2e; color: #eee; font-family: monospace; }
        h1 { color: #0f0; }
    </style>
</head>
<body>
    <h1>Welcome to Hacker Forum</h1>
    <p>The underground community for elite hackers.</p>
</body>
</html>

```

## Exports

Expose TypeScript functions and values to your HTML pages:

```typescript
@RegisterWebsite
class MyWebsite extends Website {

    SiteName = "My Site";
    Host = "mysite.com";
    Icon = "./assets/icon.png";
    Pages = [
        { path: "/", title: "Home", html: homePage },
    ];

    Exports = {
        formatPost: (text: string) => text.toUpperCase(),
        siteVersion: "1.0.0",
    };

}

```

In your HTML, exported values are available as globals:

```html
<script>
    const formatted = formatPost("hello world");
    document.getElementById("version").textContent = siteVersion;
</script>

```

## Page Navigation

Links between your website’s pages work automatically. Use standard `<a>` tags with relative or absolute URLs:

```html
<a href="/about">About Us</a>
<a href="https://hackerforum.net/posts">Posts</a>

```

Clicking a link will navigate the in-game browser to the correct page - no extra code needed.

For programmatic navigation, use the `HackhubSDK.Browser` API:

```html
<script>
    // Navigate to a page on your site
    HackhubSDK.Browser.navigate("https://hackerforum.net/about");

    // Navigate to any website in the game
    HackhubSDK.Browser.navigate("https://goagle.com");
</script>

```

| Method | Description |
| --- | --- |
| `Browser.navigate(url)` | Navigate the browser to the given URL |
| `Browser.push(url)` | Push a new URL to the browser history |
| `Browser.machine()` | `{ hostname, ip, user }` of the computer the browser runs on over remote desktop, `null` on the player’s own |

On someone else’s computer your page still runs with the player’s SDK. If `Browser.machine()` is not `null`, show the site signed out: no account details, balances or saved data. See Remote desktops.

## SDK Access in HTML

All SDK APIs are available via the `HackhubSDK` global (Context Bridge):

```html
<script>
    HackhubSDK.Mail.send({
        from: "noreply@hackerforum.net",
        subject: "Welcome",
        content: "Thanks for registering!",
    });

    HackhubSDK.Events.emit("MyMod.UserRegistered", { username: "hacker" });
</script>

```

See the [WebView Context Bridge](https://hotbunny.dev/t/webview-context-bridge/74) reference for the full list of available APIs.
