diff --git a/.changeset/wild-moose-dream.md b/.changeset/wild-moose-dream.md
new file mode 100644
index 000000000..429d74734
--- /dev/null
+++ b/.changeset/wild-moose-dream.md
@@ -0,0 +1,6 @@
+---
+'@quilted/preact-browser': patch
+'@quilted/create': patch
+---
+
+Make browser context creation implicit on the client
diff --git a/packages/create/templates/app-basic/browser.tsx b/packages/create/templates/app-basic/browser.tsx
index 33ee7220d..c5c484971 100644
--- a/packages/create/templates/app-basic/browser.tsx
+++ b/packages/create/templates/app-basic/browser.tsx
@@ -1,24 +1,15 @@
import '@quilted/quilt/globals';
-import {hydrate} from 'preact';
-import {Browser, BrowserContext} from '@quilted/quilt/browser';
+import {hydrate} from '@quilted/quilt/browser';
import {Router} from '@quilted/quilt/navigation';
import type {AppContext} from '~/shared/context.ts';
import {App} from './App.tsx';
-const element = document.querySelector('#app')!;
-const browser = new Browser();
-
const context = {
- router: new Router(browser.request.url),
+ router: new Router(),
} satisfies AppContext;
// Makes key parts of the app available in the browser console
Object.assign(globalThis, {app: context});
-hydrate(
-
-
- ,
- element,
-);
+hydrate();
diff --git a/packages/create/templates/app-basic/tests/render/render.tsx b/packages/create/templates/app-basic/tests/render/render.tsx
index 46bcddef3..5a2c742c2 100644
--- a/packages/create/templates/app-basic/tests/render/render.tsx
+++ b/packages/create/templates/app-basic/tests/render/render.tsx
@@ -1,5 +1,8 @@
import {createRender} from '@quilted/quilt/testing';
-import {BrowserContext, BrowserTestMock} from '@quilted/quilt/browser/testing';
+import {
+ BrowserDetailsContext,
+ BrowserTestMock,
+} from '@quilted/quilt/browser/testing';
import {Navigation, TestRouter} from '@quilted/quilt/navigation/testing';
import {Localization} from '@quilted/quilt/localize';
@@ -29,11 +32,11 @@ export const renderApp = createRender<
return (
-
+
{element}
-
+
);
},
diff --git a/packages/create/templates/app-graphql/browser.tsx b/packages/create/templates/app-graphql/browser.tsx
index dcc712427..ec9f43fbd 100644
--- a/packages/create/templates/app-graphql/browser.tsx
+++ b/packages/create/templates/app-graphql/browser.tsx
@@ -1,22 +1,18 @@
import '@quilted/quilt/globals';
-import {hydrate} from 'preact';
-import {createGraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
-import {Browser, BrowserContext} from '@quilted/quilt/browser';
+import {hydrate} from '@quilted/quilt/browser';
import {Router} from '@quilted/quilt/navigation';
+import {createGraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
import type {AppContext} from '~/shared/context.ts';
import {App} from './App.tsx';
-const element = document.querySelector('#app')!;
-const browser = new Browser();
-
const graphQLFetch = createGraphQLFetch({url: '/api/graphql'});
const graphQLCache = new GraphQLCache({fetch: graphQLFetch});
const context = {
- router: new Router(browser.request.url),
+ router: new Router(),
graphql: {
fetch: graphQLFetch,
cache: graphQLCache,
@@ -26,9 +22,4 @@ const context = {
// Makes key parts of the app available in the browser console
Object.assign(globalThis, {app: context});
-hydrate(
-
-
- ,
- element,
-);
+hydrate();
diff --git a/packages/create/templates/app-graphql/tests/render/render.tsx b/packages/create/templates/app-graphql/tests/render/render.tsx
index e9bf3da36..dbd63abd9 100644
--- a/packages/create/templates/app-graphql/tests/render/render.tsx
+++ b/packages/create/templates/app-graphql/tests/render/render.tsx
@@ -1,7 +1,10 @@
import {Suspense} from 'preact/compat';
import {createRender} from '@quilted/quilt/testing';
-import {BrowserContext, BrowserTestMock} from '@quilted/quilt/browser/testing';
+import {
+ BrowserDetailsContext,
+ BrowserTestMock,
+} from '@quilted/quilt/browser/testing';
import {Navigation, TestRouter} from '@quilted/quilt/navigation/testing';
import {Localization} from '@quilted/quilt/localize';
import {GraphQLCache} from '@quilted/quilt/graphql';
@@ -44,7 +47,7 @@ export const renderApp = createRender<
return (
-
+
-
+
);
},
diff --git a/packages/create/templates/app-trpc/browser.tsx b/packages/create/templates/app-trpc/browser.tsx
index 7a0f8e28b..32c74a2ab 100644
--- a/packages/create/templates/app-trpc/browser.tsx
+++ b/packages/create/templates/app-trpc/browser.tsx
@@ -1,26 +1,23 @@
import '@quilted/quilt/globals';
-import {hydrate} from 'preact';
+import {hydrate} from '@quilted/quilt/browser';
+import {Router} from '@quilted/quilt/navigation';
+
import {httpBatchLink} from '@trpc/client';
import {QueryClient} from '@tanstack/react-query';
-import {Browser, BrowserContext} from '@quilted/quilt/browser';
-import {Router} from '@quilted/quilt/navigation';
import type {AppContext} from '~/shared/context.ts';
import {trpc} from '~/shared/trpc.ts';
import {App} from './App.tsx';
-const element = document.querySelector('#app')!;
-const browser = new Browser();
-
const queryClient = new QueryClient();
const trpcClient = trpc.createClient({
links: [httpBatchLink({url: new URL('/api', window.location.href).href})],
});
const context = {
- router: new Router(browser.request.url),
+ router: new Router(),
trpc: trpcClient,
queryClient,
} satisfies AppContext;
@@ -28,9 +25,4 @@ const context = {
// Makes key parts of the app available in the browser console
Object.assign(globalThis, {app: context});
-hydrate(
-
-
- ,
- element,
-);
+hydrate();
diff --git a/packages/create/templates/app-trpc/tests/render/render.tsx b/packages/create/templates/app-trpc/tests/render/render.tsx
index a05eefff4..3da113aa5 100644
--- a/packages/create/templates/app-trpc/tests/render/render.tsx
+++ b/packages/create/templates/app-trpc/tests/render/render.tsx
@@ -1,5 +1,8 @@
import {createRender} from '@quilted/quilt/testing';
-import {BrowserContext, BrowserTestMock} from '@quilted/quilt/browser/testing';
+import {
+ BrowserDetailsContext,
+ BrowserTestMock,
+} from '@quilted/quilt/browser/testing';
import {Navigation, TestRouter} from '@quilted/quilt/navigation/testing';
import {Localization} from '@quilted/quilt/localize';
@@ -37,7 +40,7 @@ export const renderApp = createRender<
return (
-
+
@@ -47,7 +50,7 @@ export const renderApp = createRender<
-
+
);
},
diff --git a/packages/preact-browser/source/browser.tsx b/packages/preact-browser/source/browser.tsx
new file mode 100644
index 000000000..1dd023250
--- /dev/null
+++ b/packages/preact-browser/source/browser.tsx
@@ -0,0 +1,49 @@
+import {
+ hydrate as preactHydrate,
+ render as preactRender,
+ type ComponentChild,
+} from 'preact';
+
+import {Browser} from '@quilted/browser';
+
+import {BrowserDetailsContext} from './context.ts';
+
+export function render(
+ component: ComponentChild,
+ {element}: {element?: string | Element} = {},
+) {
+ const browser = new Browser();
+
+ return preactRender(
+
+ {component}
+ ,
+ resolveContainerNode(element),
+ );
+}
+
+export function hydrate(
+ component: ComponentChild,
+ {element}: {element?: string | Element} = {},
+) {
+ const browser = new Browser();
+
+ return preactHydrate(
+
+ {component}
+ ,
+ resolveContainerNode(element),
+ );
+}
+
+function resolveContainerNode(selectorOrElement: string | Element = '#app') {
+ if (typeof selectorOrElement !== 'string') return selectorOrElement;
+
+ const element = document.querySelector(selectorOrElement);
+
+ if (element == null) {
+ throw new Error(`No element found for selector: ${selectorOrElement}`);
+ }
+
+ return element;
+}
diff --git a/packages/preact-browser/source/components/BrowserContext.tsx b/packages/preact-browser/source/components/BrowserContext.tsx
deleted file mode 100644
index 1d98fe6f8..000000000
--- a/packages/preact-browser/source/components/BrowserContext.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import type {RenderableProps} from 'preact';
-import type {BrowserDetails} from '@quilted/browser';
-import {BrowserDetailsContext} from '../context.ts';
-
-/**
- * Provides details about the browser to your React app.
- */
-export function BrowserContext({
- browser,
- children,
-}: RenderableProps<{browser: BrowserDetails}>) {
- return (
-
- {children}
-
- );
-}
diff --git a/packages/preact-browser/source/index.ts b/packages/preact-browser/source/index.ts
index 44db01eb6..f7ff65854 100644
--- a/packages/preact-browser/source/index.ts
+++ b/packages/preact-browser/source/index.ts
@@ -1,5 +1,6 @@
export * from '@quilted/browser';
+export {render, hydrate} from './browser.tsx';
export {BrowserDetailsContext, useBrowserDetails} from './context.ts';
export {useAlternateUrl} from './hooks/alternate-url.ts';
@@ -17,7 +18,6 @@ export {useThemeColor} from './hooks/theme-color.ts';
export {useTitle} from './hooks/title.ts';
export {Alternate} from './components/Alternate.tsx';
-export {BrowserContext} from './components/BrowserContext.tsx';
export {BodyAttributes} from './components/BodyAttributes.tsx';
export {HTMLAttributes} from './components/HTMLAttributes.tsx';
export {Link} from './components/Link.tsx';
diff --git a/packages/preact-browser/source/testing.ts b/packages/preact-browser/source/testing.ts
index 672c8dff7..fc2f73fea 100644
--- a/packages/preact-browser/source/testing.ts
+++ b/packages/preact-browser/source/testing.ts
@@ -1,2 +1,2 @@
export * from '@quilted/browser/testing';
-export {BrowserContext} from './components/BrowserContext.tsx';
+export {BrowserDetailsContext} from './context.ts';
diff --git a/tests/e2e/fixtures/basic-app/browser.tsx b/tests/e2e/fixtures/basic-app/browser.tsx
index aca1fc678..5d3eeb88d 100644
--- a/tests/e2e/fixtures/basic-app/browser.tsx
+++ b/tests/e2e/fixtures/basic-app/browser.tsx
@@ -1,15 +1,6 @@
import '@quilted/quilt/globals';
-import {hydrate} from 'preact';
-import {Browser, BrowserContext} from '@quilted/quilt/browser';
+import {hydrate} from '@quilted/quilt/browser';
import App from './App.tsx';
-const element = document.querySelector('#app')!;
-const browser = new Browser();
-
-hydrate(
-
-
- ,
- element,
-);
+hydrate();
diff --git a/tests/e2e/integrations/trpc.test.ts b/tests/e2e/integrations/trpc.test.ts
index d183e865c..da68170f3 100644
--- a/tests/e2e/integrations/trpc.test.ts
+++ b/tests/e2e/integrations/trpc.test.ts
@@ -58,20 +58,18 @@ describe('trpc', () => {
`,
'browser.tsx': multiline`
import '@quilted/quilt/globals';
- import {hydrate} from 'preact';
- import {Browser, BrowserContext} from '@quilted/quilt/browser';
+ import {hydrate} from '@quilted/quilt/browser';
import {httpBatchLink} from '@trpc/client';
import {QueryClient} from '@tanstack/react-query';
import {App, trpc} from './App.tsx';
const element = document.querySelector('#app')!;
- const browser = new Browser();
const trpcClient = trpc.createClient({
links: [
httpBatchLink({
- url: new URL('/api', browser.request.url).href,
+ url: new URL('/api').href,
}),
],
});
@@ -79,10 +77,7 @@ describe('trpc', () => {
const queryClient = new QueryClient();
hydrate(
-
-
- ,
- element,
+ ,
);
`,
'server.tsx': multiline`