+
+
+
+ >
+ );
+}
+
+export function Head() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/packages/www/src/pages/playground/preview.tsx b/packages/www/src/pages/playground/preview.tsx
new file mode 100644
index 000000000..2dfc7d9ad
--- /dev/null
+++ b/packages/www/src/pages/playground/preview.tsx
@@ -0,0 +1,3 @@
+export default function Playground() {
+ return ;
+}
diff --git a/packages/www/src/public/content.json b/packages/www/src/public/content.json
index 7ae896226..8e0352d24 100644
--- a/packages/www/src/public/content.json
+++ b/packages/www/src/public/content.json
@@ -9,31 +9,41 @@
"id": "/api-reference#components",
"title": "Components",
"text": "Context Provider",
- "titles": ["API Reference"]
+ "titles": [
+ "API Reference"
+ ]
},
{
"id": "/api-reference#functions",
"title": "Functions",
"text": "createContext\ncreatePortal\ndangerHTML\nnavigate\nnotFound",
- "titles": ["API Reference"]
+ "titles": [
+ "API Reference"
+ ]
},
{
"id": "/api-reference#extended-properties",
"title": "Extended Properties",
"text": "debounceEvent\nindicateEvent\nkey\nref\nskipSSR\nrenderOn\nrenderMode",
- "titles": ["API Reference"]
+ "titles": [
+ "API Reference"
+ ]
},
{
"id": "/api-reference#server-apis",
"title": "Server APIs",
"text": "renderInAction\nrenderToReadableStream\nrenderToString\ngetServeOptions\nfileSystemRouter\nRenderInitator\nserve\nSSRWebComponent\nNode.js APIs\n\nserve\nhandler",
- "titles": ["API Reference"]
+ "titles": [
+ "API Reference"
+ ]
},
{
"id": "/api-reference#compiler-apis",
"title": "Compiler APIs",
"text": "compileWC",
- "titles": ["API Reference"]
+ "titles": [
+ "API Reference"
+ ]
},
{
"id": "/api-reference/brisa-cli#brisa-cli",
@@ -57,13 +67,17 @@
"id": "/api-reference/brisa-cli/brisa-add#mdx-integration",
"title": "MDX integration",
"text": "The MDX integration allows you to use MDX files in your project. To add the MDX integration to your project, run the following command inside your project directory: brisa add mdx Take a look at the MDX integration documentation to learn more about the MDX integration.",
- "titles": ["Adding integrations (brisa add)"]
+ "titles": [
+ "Adding integrations (brisa add)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-add#tailwind-css-integration",
"title": "Tailwind CSS integration",
"text": "The Tailwind CSS integration allows you to use Tailwind CSS in your project. To add the Tailwind CSS integration to your project, run the following command inside your project directory: brisa add tailwindcss Take a look at the Tailwind CSS integration documentation to learn more about the Tailwind CSS integration.",
- "titles": ["Adding integrations (brisa add)"]
+ "titles": [
+ "Adding integrations (brisa add)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#building-brisa-build",
@@ -75,49 +89,68 @@
"id": "/api-reference/brisa-cli/brisa-build#build-different-outputs",
"title": "Build different outputs",
"text": "If you want to build your application for different outputs, you can use the output field in the brisa.config.ts file. The available outputs are: server: The default output. It generates a server-side application.\nstatic: Generates a static application.\ndesktop: Generates a desktop application.\nandroid: Generates an Android application.\nios: Generates an iOS application. If you want to make a desktop app for Windows, another one for Mac, also android and ios, all at the same time. We recommend you do this in an array inside a pipeline and use an environment variable to decide the output. The good thing is that if you are on Windows, it will use the native Windows stuff for the Destkop app build, the same with Linux and Mac.",
- "titles": ["Building (brisa build)"]
+ "titles": [
+ "Building (brisa build)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#development-build",
"title": "Development build",
"text": "brisa build -d creates a development build of your application. This build is useful for custom servers.",
- "titles": ["Building (brisa build)"]
+ "titles": [
+ "Building (brisa build)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#web-component-build",
"title": "Web component build",
"text": "Brisa is more than a framework; it is a Web Component Compiler. You can create web components using Brisa and build them to create a library. brisa build -w path/web-component.tsx creates a standalone web component to create a library. The path to the file can be relative or absolute. The output will be: The name of the web component is going to be the name of the file. For example, if your file is\ncustom-counter.tsx, the name of the web component will be custom-counter. The output will be: [ wait ] 🚀 building your standalone components...\n[ info ]\n[ info ] Standalone components:\n[ info ] - build/custom-counter.server.js (646.00 B)\n[ info ] - build/custom-counter.client.js (425.00 B)\n[ info ]\n[ info ] ✨ Done in 59.78ms. In the case that you need to build more than one web component, you can use the --web-component flag multiple times: brisa build -w path/web-component1.tsx -w path/web-component2.tsx After running the command, you will have a web-component1.client.js, web-component1.server.ts, web-component2.client.js, and web-component2.server.ts file. [ wait ] 🚀 building your standalone components...\n[ info ]\n[ info ] Standalone components:\n[ info ] - build/web-component1.server.js (646.00 B)\n[ info ] - build/web-component1.client.js (425.00 B)\n[ info ] - build/web-component2.server.js (646.00 B)\n[ info ] - build/web-component2.client.js (425.00 B)\n[ info ]\n[ info ] ✨ Done in 153.72ms. Why theses files? *.client.js: The client-side code of the web component.\n*.server.ts: The server-side code of the web component, used for SSR. Even though it is called \"server\", it does not mean that it is a \"server component\", it is still a \"web component\", which is transformed to be able to do SSR of it, taking into account the Declarative Shadow DOM and the access to the WebContext.",
- "titles": ["Building (brisa build)"]
+ "titles": [
+ "Building (brisa build)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#client-side-code-usage",
"title": "Client-side code usage",
"text": "Example using these web components in Vanilla JavaScript: \n\n\n \n \n Example\n \n \n \n\n\n \n \n\n The import map is necessary outside of the Brisa framework to map brisa/client to brisa/client-simplified. This is because the Brisa client is internally used by the Brisa framework and we did a simplified version to be used outside of the framework.",
- "titles": ["Building (brisa build)", "Web component build"]
+ "titles": [
+ "Building (brisa build)",
+ "Web component build"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#ssr-of-web-component",
"title": "SSR of Web Component",
"text": "Example server-side rendering these web components in a different JSX framework: import { renderToString } from 'brisa/server';\nimport WebComponent1 from './web-component1.server.ts';\nimport WebComponent2 from './web-component1.server.ts';\n\nconst htmlWC1 = await renderToString();\nconst htmlWC2 = await renderToString(); In the case of incompatibilties with the jsx-runtime, you can use the jsx function: import { renderToString } from 'brisa/server';\nimport { jsx } from 'brisa/jsx-runtime';\nimport WebComponent1 from './web-component1.server.ts';\nimport WebComponent2 from './web-component1.server.ts';\n\nconst htmlWC1 = await renderToString(jsx(WebComponent1, { foo: \"bar\" }));\nconst htmlWC2 = await renderToString(jsx(WebComponent2, { foo: \"bar\" })); The Web Components during SSR are transformed into Declarative Shadow DOM.",
- "titles": ["Building (brisa build)", "Web component build"]
+ "titles": [
+ "Building (brisa build)",
+ "Web component build"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#component-build",
"title": "Component build",
"text": "brisa build -c path/component.ts creates a standalone server component to create a library. The path to the file can be relative or absolute. The output will be: [ wait ] 🚀 building your standalone components...\n[ info ]\n[ info ] Standalone components:\n[ info ] - build/component.server.js (446.00 B)\n[ info ]\n[ info ] ✨ Done in 53.71ms. In the case that you need to build more than one component, you can use the --component flag multiple times: brisa build -c path/component1.ts -c path/component2.ts After running the command, you will have a component1.server.ts and a component2.server.ts file. [ wait ] 🚀 building your standalone components...\n[ info ]\n[ info ] Standalone components:\n[ info ] - build/component1.server.js (446.00 B)\n[ info ] - build/component2.server.js (426.00 B)\n[ info ]\n[ info ] ✨ Done in 72.31ms.",
- "titles": ["Building (brisa build)"]
+ "titles": [
+ "Building (brisa build)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#how-to-use-the-server-component",
"title": "How to use the server component",
"text": "Example using this server component in a different framework: import { renderToString } from 'brisa/server';\nimport { Component } from 'path/component.server.ts';\n\nconst html = await renderToString(); In the case of incompatibilties with the jsx-runtime, you can use the jsx function: import { renderToString } from 'brisa/server';\nimport { jsx } from 'brisa/jsx-runtime';\n\nconst html = await renderToString(jsx(Component, { foo: 'bar' })); Server Actions are not supported in standalone components for security reasons.",
- "titles": ["Building (brisa build)", "Component build"]
+ "titles": [
+ "Building (brisa build)",
+ "Component build"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-build#skip-open-tauri-app",
"title": "Skip open Tauri app",
"text": "When the output is set to desktop, ios, or android in brisa.config.ts, the build is done twice: The first build is for the statics files.\nThe second build is for the desktop, ios, or android app (Integration with Tauri). brisa build -s skips the integration with Tauri and only builds the static files. Learn more about the app strategy (server, static, desktop, android, ios) here.",
- "titles": ["Building (brisa build)"]
+ "titles": [
+ "Building (brisa build)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-dev#development-brisa-dev",
@@ -129,19 +162,25 @@
"id": "/api-reference/brisa-cli/brisa-dev#changing-the-port",
"title": "Changing the port",
"text": "The application will start at http://localhost:3000 by default. The default port can be changed via process.env.PORT or with -p flag, like so: brisa dev -p 8080",
- "titles": ["Development (brisa dev)"]
+ "titles": [
+ "Development (brisa dev)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-dev#debug-mode",
"title": "Debug mode",
"text": "brisa dev -d enables debug mode. See more about debugging documentation.",
- "titles": ["Development (brisa dev)"]
+ "titles": [
+ "Development (brisa dev)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-dev#skip-tauri",
"title": "Skip tauri",
"text": "brisa dev -s skips opening the desktop app when output is set to desktop, ios, or android in brisa.config.ts. Learn more about the app strategy (server, static, desktop, android, ios) here.",
- "titles": ["Development (brisa dev)"]
+ "titles": [
+ "Development (brisa dev)"
+ ]
},
{
"id": "/api-reference/brisa-cli/brisa-start#production-server-brisa-start",
@@ -153,7 +192,9 @@
"id": "/api-reference/brisa-cli/brisa-start#changing-the-port",
"title": "Changing the port",
"text": "The application will start at http://localhost:3000 by default. The default port can be changed via process.env.PORT or with -p flag, like so: brisa start -p 8080",
- "titles": ["Production Server (brisa start)"]
+ "titles": [
+ "Production Server (brisa start)"
+ ]
},
{
"id": "/api-reference/compiler-apis/compileWC#compilewc",
@@ -165,43 +206,63 @@
"id": "/api-reference/compiler-apis/compileWC#reference",
"title": "Reference",
"text": "",
- "titles": ["compileWC"]
+ "titles": [
+ "compileWC"
+ ]
},
{
"id": "/api-reference/compiler-apis/compileWC#compilewccode-string-string",
"title": "compileWC(code: string): string",
"text": "The compileWC function transpiles a JSX web component to be compatible with the browser. It is very likely that in your day to day with Brisa you will not need to use this function, but if you need to do something more advanced, such as a Bundler plugin, or a Playground, this function can be useful. The Web Component is expected to be defined in the same way as it is defined inside src/web-components, that is, with an export default.",
- "titles": ["compileWC", "Reference"]
+ "titles": [
+ "compileWC",
+ "Reference"
+ ]
},
{
"id": "/api-reference/compiler-apis/compileWC#parameters",
"title": "Parameters",
"text": "code: The code of the web component.",
- "titles": ["compileWC", "Reference", "compileWC(code: string): string"]
+ "titles": [
+ "compileWC",
+ "Reference",
+ "compileWC(code: string): string"
+ ]
},
{
"id": "/api-reference/compiler-apis/compileWC#returns",
"title": "Returns",
"text": "The transpiled code of the web component.",
- "titles": ["compileWC", "Reference", "compileWC(code: string): string"]
+ "titles": [
+ "compileWC",
+ "Reference",
+ "compileWC(code: string): string"
+ ]
},
{
"id": "/api-reference/compiler-apis/compileWC#example",
"title": "Example",
"text": "import { compileWC } from \"brisa/compiler\";\n\nconst code = `\n\texport default function MyComponent() { \n\t\treturn
Hello World
;\n\t}\n`;\nconst finalCode = compileWC(code);\nconsole.log(finalCode);\n/*\n\timport {brisaElement} from \"brisa/client\";\n\t\t\t\n\tfunction MyComponent() {\n\t\treturn [\"div\", {}, \"Hello World\"];\n\t}\n\t\t\t\n\texport default brisaElement(MyComponent);\n*/",
- "titles": ["compileWC"]
+ "titles": [
+ "compileWC"
+ ]
},
{
"id": "/api-reference/compiler-apis/compileWC#outside-bunjs",
"title": "Outside Bun.js",
"text": "This function is intended to be used within the Bun runtime, as it uses the Bun transpiler to convert TSX to JS. However, if you want to use it in other environments, such as Node.js or in the browser, you can do so, but you will need to transpile the TSX to JS beforehand, for example with @swc/wasm-web. Bun.Transpiler is not applied when the environment is not Bun.js, so you will need to transpile the code before using compileWC to convert it to js.",
- "titles": ["compileWC"]
+ "titles": [
+ "compileWC"
+ ]
},
{
"id": "/api-reference/compiler-apis/compileWC#example-with-swcwasm-web",
"title": "Example with @swc/wasm-web",
"text": "import { compileWC } from \"brisa/compiler\";\nimport initSwc, { transformSync } from \"@swc/wasm-web\";\n\nasync function main() {\n\tawait initSwc();\n\tconst code = `\n\t\texport default function MyComponent() { \n\t\t\treturn
Hello World
;\n\t\t}\n\t`;\n\tconst transpiledCode = transformSync(code, {\n\t\tjsc: {\n\t\t\tparser: {\n\t\t\t\tsyntax: \"typescript\",\n\t\t\t\ttsx: true,\n\t\t\t},\n\t\t},\n\t});\n\tconst finalCode = compileWC(transpiledCode.code);\n\tconsole.log(finalCode);\n}",
- "titles": ["compileWC", "Outside Bun.js"]
+ "titles": [
+ "compileWC",
+ "Outside Bun.js"
+ ]
},
{
"id": "/api-reference/components/context-provider#context-provider",
@@ -213,13 +274,18 @@
"id": "/api-reference/components/context-provider#reference",
"title": "Reference",
"text": "",
- "titles": ["context-provider"]
+ "titles": [
+ "context-provider"
+ ]
},
{
"id": "/api-reference/components/context-provider#lesscontext-provider-contextcontext-valuefoogreaterlesscontext-providergreater",
"title": "...",
"text": "The context-provider component is required to propagate a value from some context to a sub-tree of components. The context-provider does not need any import. You can use the custom element context-provider by passing the context and value. It is a web component because this way the value is going to be shared with the web components and also you can use the same provider in web components. Server component: import { createContext } from \"brisa\";\nimport AnotherComponent from \"@/components/another-component\";\n\nconst ctx = createContext(\"foo\");\n\nexport default function ServerComponent() {\n \n \n ;\n} Web component: import { createContext } from \"brisa\";\n\nconst ctx = createContext(\"foo\");\n\nexport default function WebComponent() {\n \n \n ;\n}",
- "titles": ["context-provider", "Reference"]
+ "titles": [
+ "context-provider",
+ "Reference"
+ ]
},
{
"id": "/api-reference/components/context-provider#parameters",
@@ -246,7 +312,10 @@
"id": "/api-reference/components/context-provider#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["context-provider", "Reference"]
+ "titles": [
+ "context-provider",
+ "Reference"
+ ]
},
{
"id": "/api-reference/components/request-context#request-context",
@@ -258,79 +327,107 @@
"id": "/api-reference/components/request-context#store",
"title": "store",
"text": "The store property is an extended map where values can be stored and shared among all web components. It serves as a global state accessible by all components. Values can be set and retrieved using the store.set and store.get methods. Example setting a value: store.set(\"count\", 0); Example getting a value:
{store.get(\"count\")}
The server store only lives at request time so that any server component can access the store unless you use transferToClient, which extends the life of the store.",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#transfertoclient",
"title": "transferToClient",
"text": "The store data from request context is only available on the server. So you can store sensitive data without worrying. However, you can transfer certain data to the client side (web-components) using store.transferToClient method. import { type RequestContext } from \"brisa\";\n\nexport default async function SomeComponent({}, request: RequestContext) {\n const data = await getData(request);\n\n request.store.set(\"data\", data);\n\n // Transfer \"data\" from store to client\n request.store.transferToClient([\"data\"]);\n\n // ..\n} This allows access to these values from the web component store. This setup also enables subsequent server actions to access the same store, as the communication flows through the client: server render → client → server action → client It is a way to modify in a reactive way from a server action the web components that consume this store. You can encrypt store data if you want to transfer sensitive data to the server actions so that it cannot be accessed from the client.",
- "titles": ["Request Context", "store"]
+ "titles": [
+ "Request Context",
+ "store"
+ ]
},
{
"id": "/api-reference/components/request-context#usecontext",
"title": "useContext",
"text": "useContext: (context: BrisaContext) => { value: T } The useContext method is used to consume a context value. It takes a BrisaContext as a parameter and returns a signal containing the context value. The context value is often used for passing data between a provider and multiple consumers within a component tree. Example: const foo = useContext(context);\nreturn
{foo.value}
; For more details, refer to the context documentation. When referring to useContext, it is essential to note that this term should not be confused with the broader concept of RequestContext mentioned earlier. The useContext is a Brisa Hook for consuming context value, that is piece of data that can be shared across multiple Brisa components. The RequestContext denotes the overall environment and configuration specific to each server component, offering a unique and more comprehensive control mechanism. Understanding this distinction is crucial for a clear comprehension of our framework's architecture.",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#indicate",
"title": "indicate",
"text": "indicate(actionName: string): IndicatorSignal The indicate method is used to add it in the indicator HTML extended attribute. This indicator automatically set the brisa-request class while the indicated server action is pending. const pending = indicate('some-server-action-name');\n// ...\ncss`\n span { display: none }\n span.brisa-request { display: inline }\n`\n// ...\n<>\n \n Pending...\n>",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#parameters",
"title": "Parameters:",
"text": "string - Indicator name. It can refer to the server action. The idea is that you can use the same indicator in other components (both server and web) using the same name to relate it to the same server action. For more details, take a look to: indicate in web components, similar method but from WebContext.\nindicate[Event] HTML extended attribute to use it in server components to register the server action indicator.\nindicator HTML extended attribute to use it in any element of server/web components.",
- "titles": ["Request Context", "indicate"]
+ "titles": [
+ "Request Context",
+ "indicate"
+ ]
},
{
"id": "/api-reference/components/request-context#route",
"title": "route",
"text": "The route is the matched route of the request. You can access to: params - for dynamic routes like /[user] you can access to params.user.\nfilePath - path of your page file.\npathname - path portion of the URL.\nquery - A record of query parameters extracted from the URL.\nname - The name associated with the route.\nkind- The type of route: exact, catch-all, optional-catch-all, or dynamic.\nsrc- The source string representing the route. Example of object: {\n filePath: \"/Users/aralroca/Documents/brisa/fun/pages/blog/[slug].tsx\",\n kind: \"dynamic\",\n name: \"/blog/[slug]\",\n pathname: \"/blog/my-cool-post\",\n src: \"/blog/[slug].js\",\n params: {\n slug: \"my-cool-post\"\n }\n} Example consuming:
{route.pathname}
",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#i18n",
"title": "i18n",
"text": "i18n: I18n The i18n object provides utilities for accessing the locale and consuming translations within components. Example: const { t, locale } = i18n;\nreturn
{t(\"hello-world\")}
; For more details, refer to the i18n documentation.",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#ws",
"title": "ws",
"text": "In case you have configured WebSockets, you can access them from any server component, api route, middleware, etc. The ws is of type ServerWebSocket, where is: interface ServerWebSocket {\n readonly data: any;\n readonly readyState: number;\n readonly remoteAddress: string;\n send(message: string | ArrayBuffer | Uint8Array, compress?: boolean): number;\n close(code?: number, reason?: string): void;\n subscribe(topic: string): void;\n unsubscribe(topic: string): void;\n publish(topic: string, message: string | ArrayBuffer | Uint8Array): void;\n isSubscribed(topic: string): boolean;\n cork(cb: (ws: ServerWebSocket) => void): void;\n} Example: import { type RequestContext } from \"brisa\";\n\nexport function GET({ ws, i18n }: RequestContext) {\n const message = i18n.t(\"hello-world\");\n\n // Sending a WebSocket message from an API route\n ws.send(message);\n\n return new Response(message, {\n headers: { \"content-type\": \"text/plain\" },\n });\n} For more information see Bun's WebSockets documentation.",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#getip",
"title": "getIP",
"text": "The IP address of a given Request can be retrieved via getIP. Below it calls Bun's server.requestIP.",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#finalurl",
"title": "finalURL",
"text": "The finalURL is the URL of your page, regardless of the fact that for the users it is another one. Example, an user enter to: /es/sobre-nosotros/ But the finalURL is: /about-us Because your page is in src/pages/about-us/index.tsx",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#id",
"title": "id",
"text": "The id is the unique identifier of the request. This id is used internally by Brisa, but we expose it to you because it can be useful for tracking. Example: console.log(id); // 1edfa3c2-e101-40e3-af57-8890795dacd4",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#renderinitiator",
"title": "renderInitiator",
"text": "The renderInitiator is a string that represents the initiator of the render. It can be: RenderInitiator.SERVER_ACTION - When is the rerender by a server action.\nRenderInitiator.SPA_NAVIGATION - When the render is initiated by a SPA navigation.\nRenderInitiator.INITIAL_REQUEST - When the render is initiated by the initial request. The default value is RenderInitiator.INITIAL_REQUEST. This is useful to know how the render was initiated and to make decisions based on it, for example initializing the store only in the RenderInitiator.INITIAL_REQUEST. For API routes, the renderInitiator is always RenderInitiator.INITIAL_REQUEST. Example: import { RenderInitiator } from \"brisa/server\";\n\nexport default function ServerComponent(props, requestContext) {\n if (requestContext.renderInitiator === RenderInitiator.INITIAL_REQUEST) {\n requestContext.store.set(\"count\", 0);\n }\n\n return
{requestContext.store.get(\"count\")}
;\n}",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/request-context#css",
"title": "css",
"text": "css(strings: TemplateStringsArray, ...values: string[]): void The css template literal is used to inject CSS into the DOM. It allows developers to define styles directly within server components using a template literal. Unlike web components, this css template literal in server components does not encapsulate. This code would affect all divs on the page: Example: css`\n div {\n background-color: ${color};\n }\n`; We recommend using the css template literal for specific cases such as generating CSS animations based on dynamic JavaScript variables. For more details, refer to the Template literal css documentation.",
- "titles": ["Request Context"]
+ "titles": [
+ "Request Context"
+ ]
},
{
"id": "/api-reference/components/web-context#web-context",
@@ -342,121 +439,167 @@
"id": "/api-reference/components/web-context#store",
"title": "store",
"text": "store: ReactiveMap The store property is a reactive map where values can be stored and shared among all web components. It serves as a global state accessible by all components. Values can be set and retrieved using the store.set, store.delete, store.get and store.has methods. Example setting a value: store.set(\"count\", 0); Example getting a value:
{store.get(\"count\")}
For more details, refer to the store documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#setoptimistic",
"title": "setOptimistic",
"text": "",
- "titles": ["Web Context", "store"]
+ "titles": [
+ "Web Context",
+ "store"
+ ]
},
{
"id": "/api-reference/components/web-context#usecontext",
"title": "useContext",
"text": "useContext: (context: BrisaContext) => { value: T } The useContext method is used to consume a context value. It takes a BrisaContext as a parameter and returns a signal containing the context value. The context value is often used for passing data between a provider and multiple consumers within a component tree. Example: const foo = useContext(context);\nreturn
{foo.value}
; For more details, refer to the context documentation. When referring to useContext, it is essential to note that this term should not be confused with the broader concept of WebContext mentioned earlier. The useContext is a Brisa Hook for consuming context value, that is piece of data that can be shared across multiple Brisa components. The WebContext denotes the overall environment and configuration specific to each web component, offering a unique and more comprehensive control mechanism. Understanding this distinction is crucial for a clear comprehension of our framework's architecture.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#state",
"title": "state",
"text": "state(initialValue?: T): Signal The state method is used to declare reactive state variables. It returns a Signal that represents the state, and any changes to the state trigger reactivity updates in the associated components. Example declaration: const count = state(0); Example usage:
{count.value}
Example mutation: count.value += 1; For more details, refer to the state documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#derived",
"title": "derived",
"text": "derived(fn: () => T): Signal The derived method is useful for creating signals derived from other signals, such as state or props. It allows developers to compute values based on existing signals. Example of declaration: const doubleCount = derived(() => count.value * 2); Example of usage:
{doubleCount.value}
For more details, refer to the derived documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#effect",
"title": "effect",
"text": "effect(fn: Effect): void The effect method is used to define functions that will be executed when the component is mounted and every time a registered signal within the effect changes. It helps manage side effects such as data fetching or DOM manipulation. Example: effect(() => {\n // This log is executed every time someSignal.value changes\n console.log(`Hello ${someSignal.value}`);\n}); For more details, refer to the effect documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#cleanup",
"title": "cleanup",
"text": "cleanup(fn: Cleanup): void The cleanup method defines functions that will be executed when the component is unmounted or to clean up an effect. It helps prevent memory leaks and ensures proper cleanup. Example: effect(() => {\n window.addEventListener(\"storage\", handleOnStorage);\n cleanup(() => window.removeEventListener(\"storage\", handleOnStorage));\n});\n\ncleanup(() => console.log(\"Web Component unmounted!\")); For more details, refer to the cleanup documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#onmount",
"title": "onMount",
"text": "onMount(fn: Effect): void The onMount method is triggered only once when the component is mounted. It is useful for handling actions that should occur during the initial mount, such as setting up document events or accessing rendered DOM elements. While the effect are executed on the fly, the onMount waits until the entire web component has been rendered. Example: onMount(() => {\n console.log(\"Yeah! Component has been mounted\");\n}); For more details, refer to the onMount documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#indicate",
"title": "indicate",
"text": "indicate(actionName: string): IndicatorSignal The indicate method is used to add it in the indicator HTML extended attribute. This indicator automatically set the brisa-request class while the indicated server action is pending. const pending = indicate('some-server-action-name');\n// ...\ncss`\n span { display: none }\n span.brisa-request { display: inline }\n`\n// ...\nPending... You can also consume it as a signal to know if the server action is pending and to have more control inside the web component. const = indicate('some-server-action-name');\n// ...\n{pending.value && Pending...}",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#parameters",
"title": "Parameters:",
"text": "string - Indicator name. It can refer to the server action. The idea is that you can use the same indicator in other components (both server and web) using the same name to relate it to the same server action. For more details, take a look to: indicate in server components, similar method but from RequestContext.\nindicate[Event] HTML extended attribute to use it in server components to register the server action indicator.\nindicator HTML extended attribute to use it in any element of server/web components.",
- "titles": ["Web Context", "indicate"]
+ "titles": [
+ "Web Context",
+ "indicate"
+ ]
},
{
"id": "/api-reference/components/web-context#reset",
"title": "reset",
"text": "reset(): void The reset method is used to invoke all cleanup functions and clear all effects and cleanups from the memory of the web component. It is primarily intended for internal use and is exposed but may have limited applicability in many cases. Example: reset(); The reset method is a powerful tool that should be used judiciously. It clears all effects and cleanups, potentially affecting the web component's behavior. Ensure that its usage aligns with the desired functionality and doesn't compromise the integrity of the web component.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#css",
"title": "css",
"text": "css(strings: TemplateStringsArray, ...values: string[]): void The css template literal is used to inject reactive CSS into the DOM. It allows developers to define styles directly within web components using a template literal. The styles are encapsulated within the Shadow DOM, ensuring that they do not interfere with each other across web components. Example: css`\n div {\n background-color: ${color.value};\n }\n`; For more details, refer to the Template literal css documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#i18n",
"title": "i18n",
"text": "i18n: I18n The i18n object provides utilities for accessing the locale and consuming translations within components. Example: const { t, locale } = i18n;\nreturn
{t(\"hello-world\")}
; For more details, refer to the i18n documentation.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#route",
"title": "route",
"text": "route: Route The route object provides access to the current route's name, pathname, params, and query. Example: const { name, pathname, params, query } = route; The route object is available in both server-side rendering (SSR) and client-side rendering (CSR).",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#self",
"title": "self",
"text": "self: HTMLElement The self property in the WebContext provides access to the DOM element of the web component. It allows developers to interact directly with the component's rendered output, enabling manipulation and customization. Example: self.addEventListener(\"click\", () => {\n console.log(\"Web component clicked!\");\n}); It is important to exercise caution when directly manipulating the DOM element using the self property. This approach can lead to potential issues, such as conflicts with the reactive nature of Brisa components. Therefore, it is recommended to use this property judiciously and only when necessary. It is an empty object during SSR to use it in some specific cases like reseting shadowRoot.adoptedStyleSheets. Normally it is better to use it inside an effect to ensure that is executed only in the client-side.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#expanding-the-webcontext",
"title": "Expanding the WebContext",
"text": "The WebContext in Brisa is intentionally designed to be extensible, providing developers with the flexibility to enhance its capabilities based on project-specific requirements. This extensibility is achieved through the integration of plugins, which are custom functionalities injected into the core of each web component.",
- "titles": ["Web Context"]
+ "titles": [
+ "Web Context"
+ ]
},
{
"id": "/api-reference/components/web-context#web-context-plugins",
"title": "Web Context Plugins",
"text": "To add plugins, you must add them in the webContextPlugins named export of the /src/web-components/_integrations.(ts|tsx|js|jsx) file. Params: Receives the preceding WebContext. Plugins are executed sequentially; if it is the initial plugin, it will contain the original WebContext, whereas for subsequent plugins, it will incorporate the WebContext modified by the preceding plugin. Return: The output will be the WebContext extended by the functionalities implemented in your plugin. It is imperative to consistently return the remaining context properties to prevent potential disruptions in web-component functionality. Note that the WebContext is utilized in server-side rendering (SSR) as well. Take this into consideration, as certain extensions may not be suitable for server-side usage. Therefore, it is recommended to employ typeof window === 'undefined' to determine if the code is running on the server.",
- "titles": ["Web Context", "Expanding the WebContext"]
+ "titles": [
+ "Web Context",
+ "Expanding the WebContext"
+ ]
},
{
"id": "/api-reference/components/web-context#example-tab-synchronization",
"title": "Example: Tab Synchronization",
"text": "src/web-components/_integrations.tsx: import type { WebContextPlugin } from \"brisa\";\n\nexport const webContextPlugins: WebContextPlugin[] = [\n (ctx) => {\n ctx.store.sync = (\n key: string,\n storage: \"localStorage\" | \"sessionStorage\" = \"localStorage\",\n ) => {\n // Skip execution on server side (SSR)\n if (typeof window === \"undefined\") return;\n\n // Sync from storage to store\n const sync = (event?: StorageEvent) => {\n if (event && event.key !== key) return;\n const storageValue = window[storage].getItem(key);\n if (storageValue != null) ctx.store.set(key, JSON.parse(storageValue));\n };\n\n // Add and remove \"storage\" event listener\n ctx.effect(() => {\n window.addEventListener(\"storage\", sync);\n ctx.cleanup(() => window.removeEventListener(\"storage\", sync));\n });\n\n // Update storage when store changes\n ctx.effect(() => {\n const val = ctx.store.get(key);\n if (val != null) window[storage].setItem(key, JSON.stringify(val));\n });\n\n sync();\n };\n\n // The ctx now has a new method \"sync\" that can be used to sync a\n // store key with localStorage\n return ctx;\n },\n]; In this example, the behavior of the store property is modified, enabling any web component to possess the store with an additional sync method. This method facilitates reactive synchronization of a store entry with the localStorage. If another tab modifies the value, the update will be reflected reactively in the DOM. In any web component: export default async function WebComponent({ }, { store }: WebContext) {\n store.sync(\"count\", 'localStorage');\n\n // This value will change reactively if the localStorage\n // \"count\" item is updated.\n return store.get('count'); The approach to synchronizing tabs can be implemented in various ways: using web sockets, monitoring tab focus, or utilizing storage events, as demonstrated in this example. From Brisa's perspective, implementing specific signals for such scenarios might be too project-specific. Therefore, we offer the flexibility to extend these signals and access web component core extras for greater control.",
- "titles": ["Web Context", "Expanding the WebContext"]
+ "titles": [
+ "Web Context",
+ "Expanding the WebContext"
+ ]
},
{
"id": "/api-reference/components/web-context#example-reactive-url-params",
"title": "Example: Reactive URL Params",
"text": "This is another example to have params of the url reactive, working with SPA navigation, for example for filtering a list of items using the URL query parameters as state: src/web-components/_integrations.tsx import type { WebContext, WebContextPlugin } from \"brisa\";\n\nfunction paramsPlugin(ctx: WebContext) {\n Object.assign(ctx, {\n get params() {\n let params = ctx.state<{ [k: string]: string }>();\n\n ctx.effect(() => {\n params.value = Object.fromEntries(\n new URLSearchParams(window.location.search).entries(),\n );\n\n const navigate = (e: any) => {\n params.value = Object.fromEntries(\n new URL(e.destination.url).searchParams.entries(),\n );\n };\n\n window.navigation?.addEventListener(\"navigate\", navigate);\n ctx.cleanup(\n () => window.navigation?.removeEventListener(\"navigate\", navigate),\n );\n });\n\n return params;\n },\n });\n\n return ctx;\n}\n\nexport const webContextPlugins: WebContextPlugin[] = [paramsPlugin]; Usage: import type { WebContext } from \"brisa\";\n\nexport default function SearchResult({}, { params }: WebContext) {\n return
{params.value?.q}
;\n} A web component in the layout is not unmounted after SPA navigation. Therefore, we need the params signal to be reactive and update the component when the URL changes. Ultimately, we believe that the JavaScript community will contribute more refined signals than these examples. We encourage developers to share their signals with the community to enhance the Brisa ecosystem.",
- "titles": ["Web Context", "Expanding the WebContext"]
+ "titles": [
+ "Web Context",
+ "Expanding the WebContext"
+ ]
},
{
"id": "/api-reference/components/web-context#typescript",
"title": "TypeScript",
"text": "To extend the WebContext interface in TypeScript, create a file in the root and define the typings for your extensions: web-context.d.ts: import \"brisa\";\n\ndeclare module \"brisa\" {\n interface WebContext {\n /**\n * Augmented store with sync method\n */\n store: BaseWebContext[\"store\"] & {\n sync: (key: string, storage?: \"localStorage\" | \"sessionStorage\") => void;\n };\n\n /**\n * Reactive URL params\n */\n params: Signal<{ [k: string]: string }>;\n }\n} Modify the WebContext accordingly, and if you wish to utilize elements like the current store, you can leverage the BaseWebContext.",
- "titles": ["Web Context", "Expanding the WebContext"]
+ "titles": [
+ "Web Context",
+ "Expanding the WebContext"
+ ]
},
{
"id": "/api-reference/extended-props/debounceEvent#debounceevent",
@@ -468,25 +611,37 @@
"id": "/api-reference/extended-props/debounceEvent#reference",
"title": "Reference",
"text": "",
- "titles": ["debounce[Event]"]
+ "titles": [
+ "debounce[Event]"
+ ]
},
{
"id": "/api-reference/extended-props/debounceEvent#debounceevent400",
"title": "debounce[Event]={400}",
"text": "Brisa extends all the HTML element events (onInput, onMouseOver, onTouchStart...) to allow to debounce the action call by replacing the on prefix to debounce. console.log(e.target.value)}\n debounceInput={400}\n/> The time unit consistently remains in milliseconds. In this example, the call to the server and consequently the execution of console.log will only take place 400ms after the user ceases typing in the textbox.",
- "titles": ["debounce[Event]", "Reference"]
+ "titles": [
+ "debounce[Event]",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/debounceEvent#parameters",
"title": "Parameters:",
"text": "milliseconds - Number of milliseconds to debounce the event. Only works in the HTML elements that trigger the action, if you use it in the components as a prop it will only work if you use it inside the component to link it with the HTML element that triggers the action. The only exception is to use it in a web-component from a server component, as the web components are transformed into real HTML elements that trigger actions, then in this case it does work. This is only implemented for server actions, for browsers events inside web components it does not apply since we do not modify the original event.",
- "titles": ["debounce[Event]", "Reference", "debounce[Event]={400}"]
+ "titles": [
+ "debounce[Event]",
+ "Reference",
+ "debounce[Event]={400}"
+ ]
},
{
"id": "/api-reference/extended-props/debounceEvent#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n❌\n\n\nSSR Web Component\n❌",
- "titles": ["debounce[Event]", "Reference"]
+ "titles": [
+ "debounce[Event]",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/indicateEvent#indicateevent",
@@ -498,13 +653,18 @@
"id": "/api-reference/extended-props/indicateEvent#reference",
"title": "Reference",
"text": "",
- "titles": ["indicate[Event]"]
+ "titles": [
+ "indicate[Event]"
+ ]
},
{
"id": "/api-reference/extended-props/indicateEvent#indicateclickindicatorsignal",
"title": "indicateClick={IndicatorSignal}",
"text": "Brisa extends all the HTML element events (onInput, onMouseOver, onTouchStart...) to allow to control the pending status of the server action by replacing the on prefix to indicate. The value is the generated IndicatorSignal by the indicate method: Read more docs about indicate in Server Components.\nRead more docs about indicate in Web Components. const indicator = indicate('some-action-name')\n// ...\n console.log(e.target.value)}\n indicateInput={indicator} // IndicatorSignal\n debouceInput={300}\n/> In this example, we are registering the indicator in the onClick server action through the indicate[Event] attribute.",
- "titles": ["indicate[Event]", "Reference"]
+ "titles": [
+ "indicate[Event]",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/indicateEvent#parameters",
@@ -520,13 +680,19 @@
"id": "/api-reference/extended-props/indicateEvent#more-docs",
"title": "More docs",
"text": "For more details, take a look to: indicate method in server components.\nindicate method in web components.\nindicator HTML extended attribute to use it in any element of server/web components.",
- "titles": ["indicate[Event]", "Reference"]
+ "titles": [
+ "indicate[Event]",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/indicateEvent#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n❌\n\n\nSSR Web Component\n❌",
- "titles": ["indicate[Event]", "Reference"]
+ "titles": [
+ "indicate[Event]",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/indicator#indicator",
@@ -538,13 +704,18 @@
"id": "/api-reference/extended-props/indicator#reference",
"title": "Reference",
"text": "",
- "titles": ["indicator"]
+ "titles": [
+ "indicator"
+ ]
},
{
"id": "/api-reference/extended-props/indicator#indicatorindicatorsignalorindicatorsignal",
"title": "indicator={IndicatorSignal|IndicatorSignal[]}",
"text": "The indicator attribute allows to pass one or several IndicatorSignal generated by the indicate function. More details about the indicate function: Read more docs about indicate in Server Components.\nRead more docs about indicate in Web Components. These indicators placed in the indicator attribute allow the brisa-request class to be added during the execution of a server action, allowing interactions with CSS from the server components such as disabling buttons, showing spinners, transitions, etc. Example of CSS: const incrementing = indicate(\"increment\");\n\nreturn (\n \n); Example of CSS: button.brisa-request {\n pointer-events: none;\n cursor: not-allowed;\n background-color: rgba(0, 0, 0, 0.1);\n}\n.spinner,\n.btn-text.brisa-request {\n display: none;\n}\n.spinner.brisa-request {\n display: inline;\n}",
- "titles": ["indicator", "Reference"]
+ "titles": [
+ "indicator",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/indicator#parameters",
@@ -560,13 +731,19 @@
"id": "/api-reference/extended-props/indicator#more-docs",
"title": "More docs",
"text": "For more details, take a look to: indicate method in server components.\nindicate method in web components.\nindicateEvent HTML extended attribute to connect an indicator to a server action.",
- "titles": ["indicator", "Reference"]
+ "titles": [
+ "indicator",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/indicator#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["indicator", "Reference"]
+ "titles": [
+ "indicator",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/key#key",
@@ -578,19 +755,27 @@
"id": "/api-reference/extended-props/key#reference",
"title": "Reference",
"text": "",
- "titles": ["key"]
+ "titles": [
+ "key"
+ ]
},
{
"id": "/api-reference/extended-props/key#keystring",
"title": "key={string}",
"text": "The key is a special string attribute you need to include when creating lists of elements. Keys help Brisa identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity: const numbers = [1, 2, 3, 4, 5];\nconst listItems = numbers.map((number) => (\n
{number}
\n)); The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys: const TodoList = ({ todos }) => {\n return todos.map((todo) =>
{todo.text}
);\n}; When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort. We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state.",
- "titles": ["key", "Reference"]
+ "titles": [
+ "key",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/key#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["key", "Reference"]
+ "titles": [
+ "key",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/ref#ref",
@@ -602,19 +787,27 @@
"id": "/api-reference/extended-props/ref#reference",
"title": "Reference",
"text": "",
- "titles": ["ref"]
+ "titles": [
+ "ref"
+ ]
},
{
"id": "/api-reference/extended-props/ref#refsignal",
"title": "ref={Signal}",
"text": "When the ref attribute is used on an HTML element, you can access the current value of that ref through the ref.value property. export default ({}, { onMount, cleanup, state }: WebContext) => {\n const ref = state(null);\n\n function onClick(e) {\n console.log(\"Event via ref\", e);\n }\n\n onMount(() => ref.value.addEventListener(\"click\", onClick));\n cleanup(() => ref.value.removeEventListener(\"click\", onClick));\n\n return
Example
;\n}; If you run it on an effect, keep in mind that they run before it has been mounted and you will not yet have access to the element. If you need multi-refs for an array, you can do it this way: export default (\n { items = [] }: Props,\n { effect, state, derived }: WebContext,\n) => {\n // Every time the \"items\" property change,\n // the \"derived\" updates the refs\n const refs = derived(() =>\n Array.from({ length: items.length }).map(() => state(null)),\n );\n\n effect(() => {\n refs.value.forEach((ref, i) => {\n if (ref.value) {\n ref.value.innerHTML = `Updated ${i}`;\n }\n });\n });\n\n return (\n <>\n {items.map((item, i) => (\n
{item}
\n ))}\n >\n );\n}; Signals in Brisa, like state(), derived(), and effect() can be used without the rules of hooks. You can use them in any order and in any place in your component. This is why you can use state() inside a derived() function.",
- "titles": ["ref", "Reference"]
+ "titles": [
+ "ref",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/ref#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n❌\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["ref", "Reference"]
+ "titles": [
+ "ref",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/renderMode#rendermode",
@@ -626,19 +819,27 @@
"id": "/api-reference/extended-props/renderMode#reference",
"title": "Reference",
"text": "",
- "titles": ["renderMode"]
+ "titles": [
+ "renderMode"
+ ]
},
{
"id": "/api-reference/extended-props/renderMode#rendermodereactivity-or-transition-or-native",
"title": "renderMode={'reactivity' | 'transition' | 'native'}",
"text": "The renderMode attribute is present on the element to specify the render mode of the next document after following the link. There are three possible values: reactivity: The next document will be rendered using reactivity. It only changes the parts of the page that have changed, perserving Web Component states.\ntransition: The next document will be rendered using reactivity and also using View Transition API.\nnative: The next document will be rendered using the browser's native rendering engine. By default, the value is reactivity. Note: If the origin of the next document is different from the current document, the value will be ignored, and the next document will be rendered using the browser's native rendering engine. Example: return ;",
- "titles": ["renderMode", "Reference"]
+ "titles": [
+ "renderMode",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/renderMode#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["renderMode", "Reference"]
+ "titles": [
+ "renderMode",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/renderOn#renderon",
@@ -650,19 +851,27 @@
"id": "/api-reference/extended-props/renderOn#reference",
"title": "Reference",
"text": "",
- "titles": ["renderOn"]
+ "titles": [
+ "renderOn"
+ ]
},
{
"id": "/api-reference/extended-props/renderOn#renderonbuild-or-runtime",
"title": "renderOn={'build' | 'runtime'}",
"text": "The renderOn attribute is present on the element to specify the rendering mode of the component. There are two possible values: build: The component will be prerendered in build time.\nruntime: The component will be rendered in runtime. (default) By default, the value is runtime. Example: Imagine we have this component that makes an external request for static resources: src/components/some-component.tsx: export default async function SomeComponent() {\n const res = await fetch(/* some external service */);\n\n return
Result: {await res.json()}
\n} Then it is consumed from some page or another component with the renderOn prop as build: return ; Then, it will be automatically transformed at build-time to a return similar to this: return dangerHTML('
Result: foo
') You can also use it in Web Components, for example: return ; And it will be transformed to: return dangerHTML('Result: foo') Only the initial HTML of the web component can be prerendered, once it is hydrated it will be rendered again from the client. The rest of the properties that are passed to it must be serializable and static, as they are executed at build time. Otherwise, it will throw an error. The attribute in the web components is stripped and will not be visible in the DOM.",
- "titles": ["renderOn", "Reference"]
+ "titles": [
+ "renderOn",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/renderOn#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["renderOn", "Reference"]
+ "titles": [
+ "renderOn",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/skipSSR#skipssr",
@@ -674,19 +883,27 @@
"id": "/api-reference/extended-props/skipSSR#reference",
"title": "Reference",
"text": "",
- "titles": ["skipSSR"]
+ "titles": [
+ "skipSSR"
+ ]
},
{
"id": "/api-reference/extended-props/skipSSR#skipssrboolean",
"title": "skipSSR={boolean}",
"text": "Use skipSSR attribute to run web components only in client-side skipping the server-side rendering (SSR). There are cases where we can avoid the SSR of some web component. It makes sense for these web components that are not available in the initial rendered page, for example they appear after some web interaction, such as a modal. To do this, all web components have available the skipSSR attribute. It's true by default (this attribute does not need to be used when it is true), but you can use it to turn to false. This can be used in any web-component, either consumed from another web-component or from a server component. return ;",
- "titles": ["skipSSR", "Reference"]
+ "titles": [
+ "skipSSR",
+ "Reference"
+ ]
},
{
"id": "/api-reference/extended-props/skipSSR#support",
"title": "Support",
"text": "It can be used in both Server Components and Web Components, but the attribute must always be in a custom element, i.e. a web component.",
- "titles": ["skipSSR", "Reference"]
+ "titles": [
+ "skipSSR",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/createContext#createcontext",
@@ -698,31 +915,47 @@
"id": "/api-reference/functions/createContext#reference",
"title": "Reference",
"text": "",
- "titles": ["createContext"]
+ "titles": [
+ "createContext"
+ ]
},
{
"id": "/api-reference/functions/createContext#createcontextdefaultvalue",
"title": "createContext(defaultValue)",
"text": "Call createContext outside of any components to create a context. import { createContext } from \"brisa\";\n\nconst defaultValue = \"foo\";\nconst SomeContext = createContext(defaultValue);",
- "titles": ["createContext", "Reference"]
+ "titles": [
+ "createContext",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/createContext#parameters",
"title": "Parameters:",
"text": "defaultValue: The value that you want the context to have when there is no matching context provider in the tree above the component that reads context. If you don’t have any meaningful default value, specify null. The default value is meant as a “last resort” fallback. It is static and never changes over time.",
- "titles": ["createContext", "Reference", "createContext(defaultValue)"]
+ "titles": [
+ "createContext",
+ "Reference",
+ "createContext(defaultValue)"
+ ]
},
{
"id": "/api-reference/functions/createContext#returns",
"title": "Returns:",
"text": "createContext returns a context object. Typically, you will use this context in a context-provider component, or the useContext hook.",
- "titles": ["createContext", "Reference", "createContext(defaultValue)"]
+ "titles": [
+ "createContext",
+ "Reference",
+ "createContext(defaultValue)"
+ ]
},
{
"id": "/api-reference/functions/createContext#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["createContext", "Reference"]
+ "titles": [
+ "createContext",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/createPortal#createportal",
@@ -734,31 +967,47 @@
"id": "/api-reference/functions/createPortal#reference",
"title": "Reference",
"text": "",
- "titles": ["createPortal"]
+ "titles": [
+ "createPortal"
+ ]
},
{
"id": "/api-reference/functions/createPortal#createportalchildren-domnode",
"title": "createPortal(children, domNode)",
"text": "To create a portal, call createPortal, passing some JSX, and the DOM node where it should be rendered: import { createPortal } from \"brisa\";\n\n// ...\n\n
\n
This child is placed in the parent div.
\n {createPortal(\n
This child is placed in the document body.
,\n document.body,\n )}\n
; A portal only changes the physical placement of the DOM node. In every other way, the JSX you render into a portal acts as a child node of the Brisa component that renders it. We recommend cleanup the portal when the component is unmounted. You can do this by using the cleanup function inside the Web Context API.",
- "titles": ["createPortal", "Reference"]
+ "titles": [
+ "createPortal",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/createPortal#parameters",
"title": "Parameters:",
"text": "children: Anything that can be rendered with Brisa, such as a piece of JSX (e.g. or ), a Fragment (<>...>), a string or a number, or an array of these.\ndomNode: Some DOM node, such as those returned by document.querySelector(). The node must already exist. Passing a different DOM node during an update will cause the portal content to be recreated.",
- "titles": ["createPortal", "Reference", "createPortal(children, domNode)"]
+ "titles": [
+ "createPortal",
+ "Reference",
+ "createPortal(children, domNode)"
+ ]
},
{
"id": "/api-reference/functions/createPortal#returns",
"title": "Returns:",
"text": "createPortal returns a node that can be included into JSX or returned from a component. If Brisa encounters it in the render output, it will place the provided children inside the provided domNode.",
- "titles": ["createPortal", "Reference", "createPortal(children, domNode)"]
+ "titles": [
+ "createPortal",
+ "Reference",
+ "createPortal(children, domNode)"
+ ]
},
{
"id": "/api-reference/functions/createPortal#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n❌\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n❌",
- "titles": ["createPortal", "Reference"]
+ "titles": [
+ "createPortal",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/dangerHTML#dangerhtml",
@@ -770,13 +1019,18 @@
"id": "/api-reference/functions/dangerHTML#reference",
"title": "Reference",
"text": "",
- "titles": ["dangerHTML"]
+ "titles": [
+ "dangerHTML"
+ ]
},
{
"id": "/api-reference/functions/dangerHTML#dangerhtmlhtml-string-dangerhtmloutput",
"title": "dangerHTML(html: string): DangerHTMLOutput",
"text": "Make situations that we want to inject HTML that we have in string to the DOM. For these occasions, you can use the dangerHTML function. Since without this function it is escaped by security. import { dangerHTML } from \"brisa\";\n\nexport default function SomeComponent() {\n return (\n <>\n {/* Escaped by default (doesn't work for security): */}\n {''}\n\n {/* Force to inject an string as HTML: */}\n {dangerHTML(\n '',\n )}\n >\n );\n} Another way to use dangerHTML is to import an HTML file and inject it into the DOM. For this, you can use the with keyword to import the file with the type text. import html from \"./file.html\" with { type: \"text\" };\n\nexport default function Page() {\n return dangerHTML(html);\n} By serving the HTML in this way, only 1 chunk will be sent during the streaming of the HTML without having to waste time rendering the JSX. A better way to serve static HTML without having to render is prerendering the page or using the renderOn extended property to prerender only some parts of the page.",
- "titles": ["dangerHTML", "Reference"]
+ "titles": [
+ "dangerHTML",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/dangerHTML#parameters",
@@ -802,7 +1056,10 @@
"id": "/api-reference/functions/dangerHTML#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅",
- "titles": ["dangerHTML", "Reference"]
+ "titles": [
+ "dangerHTML",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/navigate#navigate",
@@ -814,13 +1071,18 @@
"id": "/api-reference/functions/navigate#reference",
"title": "Reference",
"text": "",
- "titles": ["navigate"]
+ "titles": [
+ "navigate"
+ ]
},
{
"id": "/api-reference/functions/navigate#navigateroute-string-options-rendermode-transition-or-reactivity-or-native-never",
"title": "navigate(route: string, options?: { renderMode?: 'transition' | 'reactivity' | 'native' }): never",
"text": "The navigate function is used for imperative navigation. import { navigate } from \"brisa\";\n\n// ...\nnavigate(\"/some-page\"); The navigate function can be used both on the client and on the server. Although there are some differences to be taken into account: If the navigation is done before sending the response (in the middleware, responseHeaders or an API endpoint for example), instead of modifying the navigation history it does a 301 redirect.\nIf it is done during rendering, a soft redirect is made.\nIf used inside a client-event or a server-event (action) a new page is always generated in the navigation history. All i18n navigaton rules apply equally in this function.",
- "titles": ["navigate", "Reference"]
+ "titles": [
+ "navigate",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/navigate#parameters",
@@ -846,7 +1108,10 @@
"id": "/api-reference/functions/navigate#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅\n\n\nActions\n✅\n\n\nMiddleware\n✅\n\n\nResponse headers\n✅",
- "titles": ["navigate", "Reference"]
+ "titles": [
+ "navigate",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/notFound#notfound",
@@ -858,31 +1123,47 @@
"id": "/api-reference/functions/notFound#reference",
"title": "Reference",
"text": "",
- "titles": ["notFound"]
+ "titles": [
+ "notFound"
+ ]
},
{
"id": "/api-reference/functions/notFound#notfound-never",
"title": "notFound(): Never",
"text": "The notFound function allows you to render the 404 page within a route segment as well as inject a tag. Invoking the notFound() function throws a NotFoundError error and terminates rendering of the route segment in which it was thrown. //src/pages/user/[id].jsx\nimport { notFound } from \"brisa\";\n\nasync function fetchUser(id) {\n const res = await fetch(\"https://...\");\n if (!res.ok) return undefined;\n return res.json();\n}\n\nexport default async function UserProfile({}, req) {\n const user = await fetchUser(req.route.params.id);\n\n if (!user) {\n notFound();\n }\n\n // ...\n}\n\n\n\n//src/pages/user/[id].tsx\nimport type { RequestContext } from \"brisa\";\nimport { notFound } from \"brisa\";\n\ntype UserType = {\n //...\n};\n\nasync function fetchUser(id: number | string) {\n const res = await fetch(\"https://...\");\n if (!res.ok) return undefined;\n return res.json();\n}\n\nexport default async function UserProfile({}, req: RequestContext) {\n const user: UserType = await fetchUser(req.route.params.id);\n\n if (!user) {\n notFound();\n }\n\n // ...\n}\n\nUseful to control response status during streaming:\n\nBefore response streaming (middleware, responseHeaders): It's returning the response with 404 status and the 404 page\nDuring response streaming (layout, page, components): Adds the meta tag with noindex, stop rendering the page and sends a client script to replace the page to the 404 page. This redirect is for UX to display the 404 content, here the bots will no longer see that because it has the noindex. However, this soft redirect that is done on the client does not change the browsing history and does receive the 404 status. The browsers normally cache very well the pages that return status 404.\nDuring a server action: (server events captured with actions): as the rendering has already been done and it is a post-render action, the 404 in an action acts similarly as in the middle of the streaming. The same happens if in the action instead of calling notFound() directly you do a rerender and the component calls notFound().\n\nParameters\n\nvoid. It does not support parameters.\n\nReturns\n\nNever does not require you to use return notFound() due to using the TypeScript never type.\n\n\n\nAvoid using the notFound inside a try/catch block. The navigate is a throwable function and will break the execution of the current function.\n\nSupport\n\n\n\nComponent\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅\n\n\nActions\n✅\n\n\nMiddleware\n✅\n\n\nResponse headers\n✅",
- "titles": ["notFound", "Reference"]
+ "titles": [
+ "notFound",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/notFound#parameters",
"title": "Parameters",
"text": "void. It does not support parameters.",
- "titles": ["notFound", "Reference", "notFound(): Never"]
+ "titles": [
+ "notFound",
+ "Reference",
+ "notFound(): Never"
+ ]
},
{
"id": "/api-reference/functions/notFound#returns",
"title": "Returns",
"text": "Never does not require you to use return notFound() due to using the TypeScript never type. Avoid using the notFound inside a try/catch block. The navigate is a throwable function and will break the execution of the current function.",
- "titles": ["notFound", "Reference", "notFound(): Never"]
+ "titles": [
+ "notFound",
+ "Reference",
+ "notFound(): Never"
+ ]
},
{
"id": "/api-reference/functions/notFound#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n✅\n\n\nWeb Component\n✅\n\n\nSSR Web Component\n✅\n\n\nActions\n✅\n\n\nMiddleware\n✅\n\n\nResponse headers\n✅",
- "titles": ["notFound", "Reference"]
+ "titles": [
+ "notFound",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/throwable#throwable",
@@ -894,43 +1175,61 @@
"id": "/api-reference/functions/throwable#reference",
"title": "Reference",
"text": "",
- "titles": ["throwable"]
+ "titles": [
+ "throwable"
+ ]
},
{
"id": "/api-reference/functions/throwable#throwableiserror-error-boolean",
"title": "throwable.is(error: Error): boolean",
"text": "The throwable.is function checks if a given error is some of the Brisa throwables: re-rendering, navigation, and not-found errors.",
- "titles": ["throwable", "Reference"]
+ "titles": [
+ "throwable",
+ "Reference"
+ ]
},
{
"id": "/api-reference/functions/throwable#example-usage",
"title": "Example usage",
"text": "import {throwable} from \"brisa\";\n\n// ...\ncatch (error) {\n if (throwable.is(error)) throw error;\n}",
- "titles": ["throwable"]
+ "titles": [
+ "throwable"
+ ]
},
{
"id": "/api-reference/functions/throwable#parameters",
"title": "Parameters:",
"text": "error: The error object that needs to be checked if it's a Throwable.",
- "titles": ["throwable", "Example usage"]
+ "titles": [
+ "throwable",
+ "Example usage"
+ ]
},
{
"id": "/api-reference/functions/throwable#returns",
"title": "Returns:",
"text": "A boolean indicating whether the provided error is an instance of the Throwable class.",
- "titles": ["throwable", "Example usage"]
+ "titles": [
+ "throwable",
+ "Example usage"
+ ]
},
{
"id": "/api-reference/functions/throwable#submethods",
"title": "Submethods",
"text": "",
- "titles": ["throwable"]
+ "titles": [
+ "throwable"
+ ]
},
{
"id": "/api-reference/functions/throwable#throwableisrerendererror-error-boolean",
"title": "throwable.isRerender(error: Error): boolean",
"text": "Determines if an error is a Throwable instance for re-rendering. import {throwable} from \"brisa\";\n\n// ...\ncatch (error) {\n if (throwable.isRerender(error)) {\n console.log(\"It's a rerender throwable!\");\n }\n}",
- "titles": ["throwable", "Submethods"]
+ "titles": [
+ "throwable",
+ "Submethods"
+ ]
},
{
"id": "/api-reference/functions/throwable#parameters-1",
@@ -956,7 +1255,10 @@
"id": "/api-reference/functions/throwable#throwableisnavigateerror-error-boolean",
"title": "throwable.isNavigate(error: Error): boolean",
"text": "Determines if an error is a Throwable instance for navigation. import {throwable} from \"brisa\";\n\n// ...\ncatch (error) {\n if (throwable.isNavigate(error)) {\n console.log(\"It's a navigate throwable!\");\n }\n}",
- "titles": ["throwable", "Submethods"]
+ "titles": [
+ "throwable",
+ "Submethods"
+ ]
},
{
"id": "/api-reference/functions/throwable#parameters-2",
@@ -982,7 +1284,10 @@
"id": "/api-reference/functions/throwable#throwableisnotfounderror-error-boolean",
"title": "throwable.isNotFound(error: Error): boolean",
"text": "Determines if an error is a Throwable instance for not-found errors. import {throwable} from \"brisa\";\n\n// ...\ncatch (error) {\n if (throwable.isNotFound(error)) {\n console.log(\"It's a not-found throwable!\");\n }\n}",
- "titles": ["throwable", "Submethods"]
+ "titles": [
+ "throwable",
+ "Submethods"
+ ]
},
{
"id": "/api-reference/functions/throwable#parameters-3",
@@ -1008,7 +1313,9 @@
"id": "/api-reference/functions/throwable#types",
"title": "Types",
"text": "interface Throwable {\n is: (error: Error) => boolean;\n isRerender: (error: Error) => boolean;\n isNavigate: (error: Error) => boolean;\n isNotFound: (error: Error) => boolean;\n}\n\nexport const throwable: Throwable;",
- "titles": ["throwable"]
+ "titles": [
+ "throwable"
+ ]
},
{
"id": "/api-reference/server-apis/fileSystemRouter#filesystemrouter",
@@ -1020,13 +1327,18 @@
"id": "/api-reference/server-apis/fileSystemRouter#reference",
"title": "Reference",
"text": "",
- "titles": ["fileSystemRouter"]
+ "titles": [
+ "fileSystemRouter"
+ ]
},
{
"id": "/api-reference/server-apis/fileSystemRouter#filesystemrouteroptions-filesystemrouteroptions-filesystemrouter",
"title": "fileSystemRouter(options: FileSystemRouterOptions): FileSystemRouter",
"text": "The fileSystemRouter function creates a new instance of the file system router.",
- "titles": ["fileSystemRouter", "Reference"]
+ "titles": [
+ "fileSystemRouter",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/fileSystemRouter#parameters",
@@ -1052,25 +1364,34 @@
"id": "/api-reference/server-apis/fileSystemRouter#example-usage",
"title": "Example usage",
"text": "import path from 'node:path';\nimport { fileSystemRouter } from 'brisa/server';\n\nconst router = fileSystemRouter({\n dir: path.join(import.meta.dirname, 'pages'),\n});\n\nconst matchedRoute = router.match('/blog/hello-world?foo=bar');\n\nif (matchedRoute) {\n console.log(matchedRoute); \n // {\n // filePath: 'pages/blog/[slug].tsx',\n // kind: 'dynamic',\n // name: '/blog/[slug]',\n // pathname: '/blog/hello-world',\n // src: 'blog/[slug].tsx',\n // params: { slug: 'hello-world' },\n // query: { foo: 'bar' },\n // }\n}",
- "titles": ["fileSystemRouter"]
+ "titles": [
+ "fileSystemRouter"
+ ]
},
{
"id": "/api-reference/server-apis/fileSystemRouter#match-method",
"title": "match method",
"text": "The match method receives a route and returns a MatchedBrisaRoute object or null.",
- "titles": ["fileSystemRouter"]
+ "titles": [
+ "fileSystemRouter"
+ ]
},
{
"id": "/api-reference/server-apis/fileSystemRouter#matchedbrisaroute",
"title": "MatchedBrisaRoute",
"text": "The MatchedBrisaRoute object has the following properties: filePath: The file path of the matched route.\nkind: The kind of route (exact, dynamic, catch-all, optional-catch-all).\nname: The route name.\npathname: The matched pathname.\nsrc: The source file path.\nparams: The route params.\nquery: The route query.",
- "titles": ["fileSystemRouter", "match method"]
+ "titles": [
+ "fileSystemRouter",
+ "match method"
+ ]
},
{
"id": "/api-reference/server-apis/fileSystemRouter#routes-property",
"title": "routes property",
"text": "The routes property is the entries of routes and file paths. routes entries are in alphabetical order import path from 'node:path';\nimport { fileSystemRouter } from 'brisa/server';\n\nconst router = fileSystemRouter({\n dir: path.join(import.meta.dirname, 'pages'),\n});\n\nconsole.log(router.routes);\n// [\n// ['/', '/Users/aralroca/my-app/src/pages/index.tsx'],\n// ['/[[...catchall]]', '/Users/aralroca/my-app/src/pages/[[...catchall]].tsx'],\n// ['/blog', '/Users/aralroca/my-app/src/pages/blog/index.tsx'],\n// ['/blog/[slug]', '/Users/aralroca/my-app/src/pages/blog/[slug].tsx'],\n// ['/settings', '/Users/aralroca/my-app/src/pages/settings.tsx'],\n// ]",
- "titles": ["fileSystemRouter"]
+ "titles": [
+ "fileSystemRouter"
+ ]
},
{
"id": "/api-reference/server-apis/getServeOptions#getserveoptions",
@@ -1082,13 +1403,18 @@
"id": "/api-reference/server-apis/getServeOptions#reference",
"title": "Reference",
"text": "",
- "titles": ["getServeOptions"]
+ "titles": [
+ "getServeOptions"
+ ]
},
{
"id": "/api-reference/server-apis/getServeOptions#getserveoptions-promiselessserveoptionsgreater",
"title": "getServeOptions(): Promise",
"text": "The getServeOptions function is used to get the serve options of Brisa to make a custom server.",
- "titles": ["getServeOptions", "Reference"]
+ "titles": [
+ "getServeOptions",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/getServeOptions#returns",
@@ -1104,7 +1430,10 @@
"id": "/api-reference/server-apis/getServeOptions#types",
"title": "Types",
"text": "ServeOptions type are all the options to use with Bun.serve.",
- "titles": ["getServeOptions", "Reference"]
+ "titles": [
+ "getServeOptions",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/node/handler#handler",
@@ -1116,25 +1445,34 @@
"id": "/api-reference/server-apis/node/handler#reference",
"title": "Reference",
"text": "",
- "titles": ["handler"]
+ "titles": [
+ "handler"
+ ]
},
{
"id": "/api-reference/server-apis/node/handler#handlerreq-httpincomingmessage-res-httpserverresponse-promiselessvoidgreater",
"title": "handler(req: http.IncomingMessage, res: http.ServerResponse): Promise",
"text": "The handler function is the user Brisa handler to handle the incoming requests. You can use it to create a custom server.",
- "titles": ["handler", "Reference"]
+ "titles": [
+ "handler",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/node/handler#example-usage",
"title": "Example usage:",
"text": "In the next example, we use the handler function to create a built-in http.createServer and set up your own custom server: import http from \"node:http\";\nimport { handler } from \"brisa/server\";\n\nasync function customServer(req, res) {\n // Your implementation here ...\n await handler(req, res);\n}\n\nconst server = http.createServer(customServer).listen(3001); Alternatively, you can use the handler function to create a custom server with Express, Connect or Polka: /// file: my-server.js\nimport { handler } from './build/handler.js';\nimport express from 'express';\n\nconst app = express();\n\n// add a route that lives separately from the SvelteKit app\napp.get('/healthcheck', (req, res) => {\n\tres.end('ok');\n});\n\n// let Brisa handle everything else, including serving prerendered pages and static assets\napp.use(handler);\n\napp.listen(3000, () => {\n\tconsole.log('listening on port 3000');\n});",
- "titles": ["handler"]
+ "titles": [
+ "handler"
+ ]
},
{
"id": "/api-reference/server-apis/node/handler#types",
"title": "Types",
"text": "export function handler(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n): Promise;",
- "titles": ["handler"]
+ "titles": [
+ "handler"
+ ]
},
{
"id": "/api-reference/server-apis/node/serve#serve",
@@ -1146,25 +1484,34 @@
"id": "/api-reference/server-apis/node/serve#reference",
"title": "Reference",
"text": "",
- "titles": ["serve"]
+ "titles": [
+ "serve"
+ ]
},
{
"id": "/api-reference/server-apis/node/serve#serve-port-port-number-port-number-hostname-string-server-returntypelesstypeof-httpcreateservergreater",
"title": "serve({ port }: { port: number }): { port: number; hostname: string; server: ReturnType; }",
"text": "The serve function is used to start the Node.js server and listen for incoming requests.",
- "titles": ["serve", "Reference"]
+ "titles": [
+ "serve",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/node/serve#example-usage",
"title": "Example usage:",
"text": "In the next example, we use the serve function to start the Node.js server. import { serve } from \"brisa/server/node\";\n\nconst { server, port, hostname } = serve({\n port: 3001,\n});\n\nconsole.log(\n \"Node.js Server ready 🥳\",\n `listening on http://${hostname}:${port}...`,\n); Keep in mind that the serve for Node.js is not in brisa/server but in brisa/server/node. It only makes sense to use it if you need a custom server for extra things from the serve but if you start the server in the same way as Brisa.",
- "titles": ["serve"]
+ "titles": [
+ "serve"
+ ]
},
{
"id": "/api-reference/server-apis/node/serve#types",
"title": "Types",
"text": "export function serve({ port }: { port: number }): {\n port: number;\n hostname: string;\n server: ReturnType;\n};",
- "titles": ["serve"]
+ "titles": [
+ "serve"
+ ]
},
{
"id": "/api-reference/server-apis/RenderInitiator#renderinitiator",
@@ -1176,13 +1523,17 @@
"id": "/api-reference/server-apis/RenderInitiator#example-usage",
"title": "Example usage:",
"text": "In the next example, we use RenderInitiator to determine if the render was initiated by a server action. import { RenderInitiator } from 'brisa';\n\nexport default function MyComponent(props, { renderInitiator }) {\n const isAnAction = renderInitiator === RenderInitiator.SERVER_ACTION;\n return (\n
\n {isAnAction ? 'This is a rerender from an action' : 'Another type of render'}\n
\n );\n}",
- "titles": ["RenderInitiator"]
+ "titles": [
+ "RenderInitiator"
+ ]
},
{
"id": "/api-reference/server-apis/RenderInitiator#types",
"title": "Types:",
"text": "export interface RenderInitiatorType {\n readonly INITIAL_REQUEST: 'INITIAL_REQUEST';\n readonly SPA_NAVIGATION: 'SPA_NAVIGATION';\n readonly SERVER_ACTION: 'SERVER_ACTION';\n}\n\nexport const RenderInitiator: RenderInitiatorType;",
- "titles": ["RenderInitiator"]
+ "titles": [
+ "RenderInitiator"
+ ]
},
{
"id": "/api-reference/server-apis/renderToReadableStream#rendertoreadablestream",
@@ -1194,13 +1545,18 @@
"id": "/api-reference/server-apis/renderToReadableStream#reference",
"title": "Reference",
"text": "",
- "titles": ["renderToReadableStream"]
+ "titles": [
+ "renderToReadableStream"
+ ]
},
{
"id": "/api-reference/server-apis/renderToReadableStream#rendertoreadablestreamelement-jsxelement-options-options-readablestreamlessanygreater",
"title": "renderToReadableStream(element: JSX.Element, options: Options): ReadableStream",
"text": "The renderToReadableStream function is used to render a JSX element to a stream on the server side.",
- "titles": ["renderToReadableStream", "Reference"]
+ "titles": [
+ "renderToReadableStream",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/renderToReadableStream#parameters",
@@ -1232,13 +1588,18 @@
"id": "/api-reference/server-apis/renderToString#reference",
"title": "Reference",
"text": "",
- "titles": ["renderToString"]
+ "titles": [
+ "renderToString"
+ ]
},
{
"id": "/api-reference/server-apis/renderToString#rendertostringelement-jsxelement-request-request-promiselessstringgreater",
"title": "renderToString(element: JSX.Element, request?: Request): Promise",
"text": "The renderToString function is used to render a JSX element to a string on the server side.",
- "titles": ["renderToString", "Reference"]
+ "titles": [
+ "renderToString",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/renderToString#parameters",
@@ -1270,13 +1631,18 @@
"id": "/api-reference/server-apis/rerenderInAction#reference",
"title": "Reference",
"text": "",
- "titles": ["rerenderInAction"]
+ "titles": [
+ "rerenderInAction"
+ ]
},
{
"id": "/api-reference/server-apis/rerenderInAction#rerenderinpage-type-rendermode-props-rerenderinaction-never",
"title": "rerenderInPage({ type, renderMode, props }: RerenderInAction): Never",
"text": "The rerenderInAction method is used to rerender the component or the page\ninside a server action. Outside of an action, it throws an error. rerenderInAction needs to be called outside of the try/catch block: import { rerenderInAction } from \"brisa/server\";\n\n// Inside a server action\nfunction handleEvent() {\n try {\n // ...\n } catch (error) {\n // ...\n }\n\n // Trigger a full-page rerender\n rerenderInAction({ type: \"page\" });\n} Example of Component Rerender: export default function MyComponent({ text = \"foo\" }: { text: string }) {\n function handleClick() {\n // Trigger a component rerender with new props\n rerenderInAction({ type: \"targetComponent\", props: { text: \"bar\" } });\n }\n\n return (\n
\n \n
\n );\n} See the differences between \"Action Signals\" and rerenderInAction in this documentation.",
- "titles": ["rerenderInAction", "Reference"]
+ "titles": [
+ "rerenderInAction",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/rerenderInAction#types",
@@ -1312,7 +1678,10 @@
"id": "/api-reference/server-apis/rerenderInAction#support",
"title": "Support",
"text": "Component\nSupport\n\n\n\n\nServer Component\n❌\n\n\nWeb Component\n❌\n\n\nSSR Web Component\n❌\n\n\nActions\n✅\n\n\nMiddleware\n❌\n\n\nResponse headers\n❌",
- "titles": ["rerenderInAction", "Reference"]
+ "titles": [
+ "rerenderInAction",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/serve#serve",
@@ -1324,25 +1693,34 @@
"id": "/api-reference/server-apis/serve#reference",
"title": "Reference",
"text": "",
- "titles": ["serve"]
+ "titles": [
+ "serve"
+ ]
},
{
"id": "/api-reference/server-apis/serve#serveoptions-serveoptions-port-number-hostname-string-server-server",
"title": "serve(options: ServeOptions): { port: number; hostname: string; server: Server; }",
"text": "The serve function is used to start the Bun.js server and listen for incoming requests.",
- "titles": ["serve", "Reference"]
+ "titles": [
+ "serve",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/serve#example-usage",
"title": "Example usage:",
"text": "In the next example, we use the serve function to start the Bun.js server. import { getServeOptions, serve } from \"brisa/server\";\n\nconst serveOptions = await getServeOptions();\n\nconst { server, port, hostname } = serve({\n ...serveOptions,\n fetch(req, server) {\n // Your implementation here ...\n\n // Brisa handler\n return serveOptions.fetch(req, server);\n },\n port: 3001,\n});\n\nconsole.log(\n \"Server ready 🥳\",\n `listening on http://${hostname}:${port}...`,\n); It only makes sense to use it if you need a custom server for extra things from the serve but if you start the server in the same way as Brisa.",
- "titles": ["serve"]
+ "titles": [
+ "serve"
+ ]
},
{
"id": "/api-reference/server-apis/serve#types",
"title": "Types",
"text": "export function serve(options: ServeOptions): {\n port: number;\n hostname: string;\n server: Server; // Bun.js server (Bun.serve)\n};",
- "titles": ["serve"]
+ "titles": [
+ "serve"
+ ]
},
{
"id": "/api-reference/server-apis/SSRWebComponent#ssrwebcomponent",
@@ -1354,25 +1732,34 @@
"id": "/api-reference/server-apis/SSRWebComponent#reference",
"title": "Reference",
"text": "",
- "titles": ["SSRWebComponent"]
+ "titles": [
+ "SSRWebComponent"
+ ]
},
{
"id": "/api-reference/server-apis/SSRWebComponent#ssrwebcomponent-1",
"title": "SSRWebComponent",
"text": "The SSRWebComponent is a component wrapper that allows you to render a web component on the server side taking care of Declarative Shadow DOM and Custom Elements.",
- "titles": ["SSRWebComponent", "Reference"]
+ "titles": [
+ "SSRWebComponent",
+ "Reference"
+ ]
},
{
"id": "/api-reference/server-apis/SSRWebComponent#example-usage",
"title": "Example usage:",
"text": "In the next example, we use the SSRWebComponent to render a web component on the server side. import { SSRWebComponent } from \"brisa/server\";\nimport MyComponent from \"@/web-components/my-component\";\n\nexport function MyComponent() {\n // It's the same than: \n // but without compilation process:\n return (\n \n );\n} This work is usually done by Brisa for you during compilation, so you can use directly in your code without having to do 2 imports and use this wrapper. However, it is exposed in case someone needs to do it manually for some reason. The selector prop is required and must match the web component's tag name, also the Component prop is required and must be the web component itself.",
- "titles": ["SSRWebComponent"]
+ "titles": [
+ "SSRWebComponent"
+ ]
},
{
"id": "/api-reference/server-apis/SSRWebComponent#types",
"title": "Types",
"text": "export function SSRWebComponent(\n props: T & { selector: string, Component: ComponentType, children?: JSX.Element },\n): JSX.Element;",
- "titles": ["SSRWebComponent"]
+ "titles": [
+ "SSRWebComponent"
+ ]
},
{
"id": "/building-your-application/authentication#authentication",
@@ -1390,13 +1777,17 @@
"id": "/building-your-application/authentication/authentication#authentication-strategies",
"title": "Authentication Strategies",
"text": "Contemporary web applications commonly employ various authentication approaches: OAuth/OpenID Connect (OIDC): Facilitates third-party access without disclosing user credentials, ideal for social media logins and Single Sign-On (SSO) solutions. It introduces an identity layer through OpenID Connect.\nCredentials-based login (Email + Password): A standard choice where users log in using email and password, familiar and straightforward to implement, necessitating robust security measures against threats like phishing.\nPasswordless/Token-based authentication: Use email magic links or SMS one-time codes for secure, password-free access. Popular for its convenience and heightened security, though reliant on user email or phone availability.\nPasskeys/WebAuthn: Utilizes site-specific cryptographic credentials, offering strong protection against phishing. While secure, its newness might pose implementation challenges. Choosing an authentication approach should align with your application's specific needs, user interface considerations, and security goals.",
- "titles": ["Authentication"]
+ "titles": [
+ "Authentication"
+ ]
},
{
"id": "/building-your-application/authentication/authentication#implementing-authentication",
"title": "Implementing Authentication",
"text": "In this section, we'll explore the process of adding basic email-password authentication to a web application. While this method provides a fundamental level of security, it's worth considering more advanced options like OAuth or passwordless logins for enhanced protection against common security threats. The authentication flow we'll discuss is as follows: The user submits their credentials through a login form.\nThe form calls a Server Action.\nUpon successful verification, the process is completed, indicating the user's successful authentication.\nIf verification is unsuccessful, an error message is shown. Consider a server component with login form where users can input their credentials: import { navigate, type RequestContext } from \"brisa\";\nimport { rerenderInAction } from \"brisa/server\";\nimport signIn from \"@/utils/auth/sign-in\";\n\nexport default function LoginPage({}, request: RequestContext) {\n const errorMsg = request.store.get(\"auth-error\");\n\n async function authenticate(e) {\n const email = e.formData.get(\"email\");\n const password = e.formData.get(\"password\");\n const success = await signIn(email, password);\n\n if (success) navigate(\"/admin\");\n\n request.store.set(\"auth-error\", \"Invalid credentials\");\n rerenderInAction({ type: \"page\" });\n }\n\n return (\n \n );\n} The form above has two input fields for capturing the user's email and password. On submission, it calls the authenticate Server Action. You can then call your Authentication Provider's API in the Server Action to handle authentication: const success = await signIn(email, password); In this code, the signIn method checks the credentials against stored user data.\nAfter the authentication provider processes the credentials, there are two possible outcomes: Successful Authentication: This outcome implies that the login was successful. Further actions, such as accessing protected routes and fetching user information, can then be initiated.\nFailed Authentication: In cases where the credentials are incorrect or an error is encountered, the function returns a corresponding error message to indicate the authentication failure. Finally, you can navigate to another page or use the request store to save form errors, and use rerenderInAction to render these errors on the form: if (success) navigate(\"/admin\");\n\nrequest.store.set(\"auth-error\", \"Invalid credentials\");\nrerenderInAction({ type: \"page\" }); Both navigate and rerenderInAction throw an exception returning a Never type, therefore the code after is not executed so there is no need to put an else conditional. It is important to keep this in mind because if it is put in a try-catch they would stop working unless from the catch you throw again these actions.",
- "titles": ["Authentication"]
+ "titles": [
+ "Authentication"
+ ]
},
{
"id": "/building-your-application/authentication/authorization#authorization",
@@ -1408,25 +1799,33 @@
"id": "/building-your-application/authentication/authorization#protecting-routes-with-middleware",
"title": "Protecting Routes with Middleware",
"text": "Middleware in Brisa helps you control who can access different parts of your website. This is important for keeping areas like the user dashboard protected while having other pages like marketing pages be public. It's recommended to apply Middleware across all routes and specify exclusions for public access. Here's how to implement Middleware for authentication in Brisa: src/middleware.ts: import type { RequestContext } from \"brisa\";\nimport parseCookies from \"@/utils/auth/parse-cookies\";\n\nexport default async function middleware(req: RequestContext) {\n // Early return for assets (no route) and api endpoints\n if (!req.route || req.route.name.startsWith(\"/api/\")) return;\n\n const cookies = parseCookies(req.headers.get(\"cookie\"));\n const currentUser = cookies.get(\"currentUser\");\n const pathname = req.route.pathname ?? \"\";\n\n if (currentUser && !pathname.startsWith(\"/dashboard\")) {\n return new Response(\"\", {\n status: 302,\n headers: {\n Location: new URL(\"/dashboard\", req.url).toString(),\n },\n });\n }\n\n if (!currentUser && !pathname.startsWith(\"/login\")) {\n return new Response(\"\", {\n status: 302,\n headers: {\n Location: new URL(\"/login\", req.url).toString(),\n },\n });\n }\n // ...\n} This example returns a Response for handling redirects early in the request pipeline, making it efficient and centralizing access control. In Brisa the middleware works in a different way than in many frameworks. If you return nothing or undefined, it continues processing the route as if it had not entered the middleware, similar to the next() function of many frameworks. Another thing you can return, is a Response, then the middleware cuts and finishes processing the route here. If what you want to do is to modify the response headers that route will have, you have to do it using the responseHeaders method. After successful authentication, it's important to manage user navigation based on their roles. For example, an admin user might be redirected to an admin dashboard, while a regular user is sent to a different page. This is important for role-specific experiences and conditional navigation, such as prompting users to complete their profile if needed. When setting up authorization, it's important to ensure that the main security checks happen where your app accesses or changes data. While Middleware can be useful for initial validation, it should not be the sole line of defense in protecting your data. The bulk of security checks should be performed in the Data Access Layer (DAL). This approach advocates for consolidating all data access within a dedicated DAL. This strategy ensures consistent data access, minimizes authorization bugs, and simplifies maintenance. To ensure comprehensive security, consider the following key areas: Server Actions: Implement security checks in server-side processes, especially for sensitive operations.\nRoute Handlers: Manage incoming requests with security measures to ensure access is limited to authorized users.\nData Access Layer (DAL): Directly interacts with the database and is crucial for validating and authorizing data transactions. It's vital to perform critical checks within the DAL to secure data at its most crucial interaction point—access or modification.",
- "titles": ["Authorization"]
+ "titles": [
+ "Authorization"
+ ]
},
{
"id": "/building-your-application/authentication/authorization#protecting-server-actions",
"title": "Protecting Server Actions",
"text": "It is important to treat Server Actions with the same security considerations as public-facing API endpoints. Verifying user authorization for each action is crucial. Implement checks within Server Actions to determine user permissions, such as restricting certain actions to admin users. In the example below, we check the user's role before allowing the action to proceed: import type { RequestContext } from \"brisa\";\nimport parseCookies from \"@/utils/auth/parse-cookies\";\nimport db from \"./lib/db\";\n\nasync function getSession(req: RequestContext) {\n const cookies = parseCookies(req.headers.get(\"cookie\"));\n const sessionId = cookies.get(\"sessionId\")?.value;\n return sessionId ? await db.findSession(sessionId) : null;\n}\n\nexport default function AdminDashboard({}, request: RequestContext) {\n async function someAction(e) {\n const session = await getSession(request);\n const userRole = session?.user?.role;\n\n if (userRole !== \"admin\") {\n throw new Error(\n \"Unauthorized access: User does not have admin privileges.\",\n );\n }\n // ...\n }\n\n return (\n
\n \n
\n );\n} The component request param is different when rendered during the SSR than when the action is called. When the action is called, it can be used as the request of the action.",
- "titles": ["Authorization"]
+ "titles": [
+ "Authorization"
+ ]
},
{
"id": "/building-your-application/authentication/authorization#protecting-route-handlers",
"title": "Protecting Route Handlers",
"text": "Route Handlers in Brisa play a vital role in managing incoming requests. Just like Server Actions, they should be secured to ensure that only authorized users can access certain functionalities. This often involves verifying the user's authentication status and their permissions. Here's an example of securing a Route Handler: src/api/route.ts: import type { RequestContext } from \"brisa\";\nimport parseCookies from \"@/utils/auth/parse-cookies\";\nimport db from \"./lib/db\";\n\nasync function getSession(req: RequestContext) {\n const cookies = parseCookies(req.headers.get(\"cookie\"));\n const sessionId = cookies.get(\"sessionId\")?.value;\n return sessionId ? await db.findSession(sessionId) : null;\n}\n\nexport async function GET(request: RequestContext) {\n // User authentication and role verification\n const session = await getSession(request);\n\n // Check if the user is authenticated\n if (!session) {\n // User is not authenticated\n return new Response(null, { status: 401 });\n }\n\n // Check if the user has the 'admin' role\n if (session.user.role !== \"admin\") {\n // User is authenticated but does not have the right permissions\n return new Response(null, { status: 403 });\n }\n\n // Data fetching for authorized users\n}",
- "titles": ["Authorization"]
+ "titles": [
+ "Authorization"
+ ]
},
{
"id": "/building-your-application/authentication/authorization#protecting-server-components",
"title": "Protecting Server Components",
"text": "Like actions and API routes, you can manage authorization within Server Components. Server Components in Brisa are designed for server-side execution and offer a secure environment for integrating complex logic like authorization. They enable direct access to back-end resources, optimizing performance for data-heavy tasks and enhancing security for sensitive operations. In Server Components, a common practice is to conditionally render UI elements based on the user's role. This approach enhances user experience and security by ensuring users only access content they are authorized to view. import type { RequestContext } from \"brisa\";\nimport parseCookies from \"@/utils/auth/parse-cookies\";\nimport AdminDashboard from \"@/components/admin-dashboard\";\nimport UserDashboard from \"@/components/user-dashboard\";\nimport AccessDenied from \"@/components/access-denied\";\nimport db from \"./lib/db\";\n\ntype Props = {\n /* .... */\n};\n\nasync function getSession(req: RequestContext) {\n const cookies = parseCookies(req.headers.get(\"cookie\"));\n const sessionId = cookies.get(\"sessionId\")?.value;\n return sessionId ? await db.findSession(sessionId) : null;\n}\n\nexport default async function Dashboard(props: Props, request: RequestContext) {\n const session = await getSession(request);\n const userRole = session?.user?.role; // Assuming 'role' is part of the session object\n\n if (userRole === \"admin\") {\n return ; // Component for admin users\n } else if (userRole === \"user\") {\n return ; // Component for regular users\n } else {\n return ; // Component shown for unauthorized access\n }\n}",
- "titles": ["Authorization"]
+ "titles": [
+ "Authorization"
+ ]
},
{
"id": "/building-your-application/authentication/session-tracking#session-tracking",
@@ -1438,13 +1837,17 @@
"id": "/building-your-application/authentication/session-tracking#cookie-based",
"title": "Cookie-based",
"text": "Cookie-based session tracking relies on HTTP cookies to store and retrieve session identifiers. When a user logs in, a unique session identifier is generated and stored within a cookie on the user's device. Subsequently, with each request sent to the server, this identifier is transmitted back, allowing the server to associate the request with the corresponding session data. Advantages: Lightweight implementation.\nWidely supported by web browsers.\nRequires minimal server-side storage. Limitations: Vulnerable to security risks such as session hijacking and cross-site scripting (XSS) attacks.\nDependency on client-side storage, which can be manipulated or disabled by users.",
- "titles": ["Session Tracking"]
+ "titles": [
+ "Session Tracking"
+ ]
},
{
"id": "/building-your-application/authentication/session-tracking#database-sessions",
"title": "Database Sessions",
"text": "Database session tracking involves storing session data directly within a database on the server-side. Upon user authentication, a unique session identifier is generated and associated with a record in the database containing relevant session information. Subsequent requests from the user include this identifier, allowing the server to retrieve the associated session data from the database. Advantages: Enhanced security compared to cookie-based sessions, as session data is stored securely on the server-side.\nGreater flexibility in managing and manipulating session data.\nSuitable for applications requiring robust session management capabilities. Limitations: Increased server-side resource utilization, particularly for high-traffic applications.\nPotential performance overhead due to database operations.\nComplexity in implementation and maintenance compared to cookie-based sessions.",
- "titles": ["Session Tracking"]
+ "titles": [
+ "Session Tracking"
+ ]
},
{
"id": "/building-your-application/building#building",
@@ -1456,25 +1859,34 @@
"id": "/building-your-application/building#production-builds",
"title": "Production Builds",
"text": "Running brisa build generates an optimized version of your application for production. HTML, CSS, and JavaScript files are created based on your pages. JavaScript is compiled and browser bundles are minified using the Bun Compiler to help achieve the best performance.",
- "titles": ["Building"]
+ "titles": [
+ "Building"
+ ]
},
{
"id": "/building-your-application/building#bun-server",
"title": "Bun Server",
"text": "Brisa can be deployed to any hosting provider that supports Bun. Ensure your package.json has the \"build\" and \"start\" scripts: {\n \"scripts\": {\n \"dev\": \"brisa dev\",\n \"build\": \"brisa build\",\n \"start\": \"brisa start\"\n }\n} Then, run bun run build to build your application. Finally, run bun run start to start the Bun server. This server supports all Brisa features.",
- "titles": ["Building", "Production Builds"]
+ "titles": [
+ "Building",
+ "Production Builds"
+ ]
},
{
"id": "/building-your-application/building#app-strategy-static-server-desktop-android-ios",
"title": "App Strategy (Static, Server, Desktop, Android, iOS)",
"text": "Brisa supports multiple output strategies to build your application. You can choose between: Build a Bun Server App\nBuild a Node.js Server App\nBuild a Static Site App\nBuild a Desktop App\nBuild a Android App\nBuild a iOS App",
- "titles": ["Building"]
+ "titles": [
+ "Building"
+ ]
},
{
"id": "/building-your-application/building#web-component-compiler",
"title": "Web Component Compiler",
"text": "Brisa is more than a framework; it is a Web Component Compiler. You can create web components using Brisa and build them to create a library. Web Component Compiler",
- "titles": ["Building"]
+ "titles": [
+ "Building"
+ ]
},
{
"id": "/building-your-application/building/android-app#android-app",
@@ -1486,13 +1898,17 @@
"id": "/building-your-application/building/android-app#brisa-android-applications-tauri-integration",
"title": "Brisa Android Applications (Tauri integration)",
"text": "This documentation outlines the process of building a Brisa android application using Tauri. Tauri is seamlessly integrated into Brisa by configuring the brisa.config.ts file as follows: import type { Configuration } from \"brisa\";\n\nexport default {\n output: \"android\",\n} satisfies Configuration; To initialize the development environment, run the following command: brisa dev Prerequisits: You need to download Android Studio and follow these steps from Tauri documentation. Executing this command launches a android app, integrating your web application. The development environment supports hot-reloading, mirroring the behavior of a browser. Notably, the integration creates a src-tauri folder, representing the fusion of Brisa with Tauri. Customizing the icons, title, and other attributes can be achieved by modifying the src-tauri/tauri.conf.json file. Explore Tauri's configuration fields here.",
- "titles": ["Android app"]
+ "titles": [
+ "Android app"
+ ]
},
{
"id": "/building-your-application/building/android-app#building-your-brisa-android-app",
"title": "Building your Brisa Android App",
"text": "When the output: \"android\" configuration is set in your brisa.config.ts, execute the following command to build the application: bun run build This command generates the corresponding .apk. The build behavior is akin to static export, as there won't be an active server, and the android app is created with the bundled assets (HTML, CSS, JS). Pure server-related functionalities, such as API endpoints and server interactions, will not function at runtime. All interactions should be encapsulated within web components.",
- "titles": ["Android app"]
+ "titles": [
+ "Android app"
+ ]
},
{
"id": "/building-your-application/building/bun-server#bun-server",
@@ -1504,19 +1920,25 @@
"id": "/building-your-application/building/bun-server#configuration-optional",
"title": "Configuration (Optional)",
"text": "To enable a web service application, change the output mode inside brisa.config.ts (optional since it is the default value): import type { Configuration } from \"brisa\";\n\nexport default {\n output: \"bun\", // It is the default value\n} satisfies Configuration; After running brisa build, Brisa will generate a Bun server that serves your application on the port 3000 by default.",
- "titles": ["Bun Server"]
+ "titles": [
+ "Bun Server"
+ ]
},
{
"id": "/building-your-application/building/bun-server#changing-the-port",
"title": "Changing the port",
"text": "To change the port, you can use the flag --port: brisa start --port 8080 The default port is process.env.PORT or 3000. After running brisa build, Brisa will generate a Bun server that serves your application on the port 8080. If you want, you can use NODE_ENV=production bun run build/server.js to start your application with Bun.js without Brisa CLI.",
- "titles": ["Bun Server"]
+ "titles": [
+ "Bun Server"
+ ]
},
{
"id": "/building-your-application/building/bun-server#custom-server",
"title": "Custom server",
"text": "If you want to use a custom server, you can follow this guide: Custom Server.",
- "titles": ["Bun Server"]
+ "titles": [
+ "Bun Server"
+ ]
},
{
"id": "/building-your-application/building/desktop-app#desktop-app",
@@ -1528,19 +1950,26 @@
"id": "/building-your-application/building/desktop-app#brisa-desktop-applications-tauri-integration",
"title": "Brisa Desktop Applications (Tauri integration)",
"text": "This documentation outlines the process of building a Brisa desktop application using Tauri. Tauri is seamlessly integrated into Brisa by configuring the brisa.config.ts file as follows: import type { Configuration } from \"brisa\";\n\nexport default {\n output: \"desktop\",\n} satisfies Configuration; To initialize the development environment, run the following command: brisa dev Executing this command launches a desktop app, integrating your web application. The development environment supports hot-reloading, mirroring the behavior of a browser. Notably, the integration creates a src-tauri folder, representing the fusion of Brisa with Tauri. Customizing the window size, icons, title, and other attributes can be achieved by modifying the src-tauri/tauri.conf.json file. Explore Tauri's configuration fields here.",
- "titles": ["Desktop app"]
+ "titles": [
+ "Desktop app"
+ ]
},
{
"id": "/building-your-application/building/desktop-app#building-your-brisa-desktop-app",
"title": "Building your Brisa Desktop App",
"text": "When the output: \"desktop\" configuration is set in your brisa.config.ts, execute the following command to build the application: bun run build This command generates the corresponding executables tailored to your operating system. The supported platforms include: Windows: -setup.exe, .msi\nmacOS: .app, .dmg\nLinux: .deb, .appimage The build behavior is akin to static export, as there won't be an active server, and the desktop app is created with the bundled assets (HTML, CSS, JS). Pure server-related functionalities, such as API endpoints and server interactions, will not function at runtime. All interactions should be encapsulated within web components.",
- "titles": ["Desktop app"]
+ "titles": [
+ "Desktop app"
+ ]
},
{
"id": "/building-your-application/building/desktop-app#cross-platform-build",
"title": "Cross-Platform Build",
"text": "Tauri relies on native libraries for each OS, preventing a direct cross-platform build. A cross-platform build can be achieved through a matrix-based pipeline. # ...\nstrategy:\n fail-fast: false\n matrix:\n platform: [macos-latest, ubuntu-20.04, windows-latest]\nruns-on: ${{ matrix.platform }}\n# ... Here's the full example YAML configuration for GitHub Actions. The GitHub Token is automatically issued by GitHub for each workflow run without further configuration, ensuring no risk of secret leakage. For more details, refer to the Tauri documentation on cross-platform builds.",
- "titles": ["Desktop app", "Building your Brisa Desktop App"]
+ "titles": [
+ "Desktop app",
+ "Building your Brisa Desktop App"
+ ]
},
{
"id": "/building-your-application/building/ios-app#ios-app",
@@ -1552,13 +1981,17 @@
"id": "/building-your-application/building/ios-app#brisa-ios-applications-tauri-integration",
"title": "Brisa iOS Applications (Tauri integration)",
"text": "This documentation outlines the process of building a Brisa ios application using Tauri. Tauri is seamlessly integrated into Brisa by configuring the brisa.config.ts file as follows: import type { Configuration } from \"brisa\";\n\nexport default {\n output: \"ios\",\n} satisfies Configuration; To initialize the development environment, run the following command: brisa dev Prerequisits: You need to download iOS targets and follow these steps from Tauri documentation. Executing this command launches a ios app, integrating your web application. The development environment supports hot-reloading, mirroring the behavior of a browser. Notably, the integration creates a src-tauri folder, representing the fusion of Brisa with Tauri. Customizing the icons, title, and other attributes can be achieved by modifying the src-tauri/tauri.conf.json file. Explore Tauri's configuration fields here.",
- "titles": ["iOS app"]
+ "titles": [
+ "iOS app"
+ ]
},
{
"id": "/building-your-application/building/ios-app#building-your-brisa-ios-app",
"title": "Building your Brisa iOS App",
"text": "When the output: \"ios\" configuration is set in your brisa.config.ts, execute the following command to build the application: bun run build This command generates the corresponding .ipa (iOS App Store Package). The build behavior is akin to Static Site App, as there won't be an active server, and the ios app is created with the bundled assets (HTML, CSS, JS). Pure server-related functionalities, such as API endpoints and server interactions, will not function at runtime. All interactions should be encapsulated within web components.",
- "titles": ["iOS app"]
+ "titles": [
+ "iOS app"
+ ]
},
{
"id": "/building-your-application/building/node-server#nodejs-server",
@@ -1570,19 +2003,25 @@
"id": "/building-your-application/building/node-server#configuration-optional",
"title": "Configuration (Optional)",
"text": "To enable a web service application, change the output mode inside brisa.config.ts: import type { Configuration } from \"brisa\";\n\nexport default {\n output: \"node\",\n} satisfies Configuration; After running brisa build, Brisa will generate a Node.js server that serves your application on the port 3000 by default.",
- "titles": ["Node.js Server"]
+ "titles": [
+ "Node.js Server"
+ ]
},
{
"id": "/building-your-application/building/node-server#changing-the-port",
"title": "Changing the port",
"text": "To change the port, you can use the flag --port: brisa start --port 8080 The default port is process.env.PORT or 3000. After running brisa build, Brisa will generate a Bun server that serves your application on the port 8080. Although you can still use the Bun tooling to start your application in Node.js, if you want, you can use NODE_ENV=production node build/server.js to start your application with Node.js without Brisa CLI.",
- "titles": ["Node.js Server"]
+ "titles": [
+ "Node.js Server"
+ ]
},
{
"id": "/building-your-application/building/node-server#custom-server",
"title": "Custom server",
"text": "If you want to use a custom server, you can follow this guide: Custom Server.",
- "titles": ["Node.js Server"]
+ "titles": [
+ "Node.js Server"
+ ]
},
{
"id": "/building-your-application/building/static-site-app#static-site-app",
@@ -1594,13 +2033,17 @@
"id": "/building-your-application/building/static-site-app#configuration",
"title": "Configuration",
"text": "To enable a static export, change the output mode inside brisa.config.ts: import type { Configuration } from \"brisa\";\n\nexport default {\n output: \"static\",\n} satisfies Configuration; After running brisa build, Brisa will produce an out folder which contains the HTML/CSS/JS assets for your application. Note that without a server, redirects such as trailingSlash will not work. You will have to manage this type of redirects on the host where you upload your static files. When generating the static files, all pages are called with the request new Request('localhost:3000'), without any configuration option. The middleware (if you have it) will only act during the build and never in runtime. Pure server stuff like api endpoints and server interactions will not work in runtime. All the interaction part should be in web-components only.",
- "titles": ["Static Site App"]
+ "titles": [
+ "Static Site App"
+ ]
},
{
"id": "/building-your-application/building/static-site-app#hard-redirects",
"title": "Hard redirects",
"text": "Redirects are no longer done through a server since there is no server once you make a static export. However, to support i18n and let the apps work when you want to put them in a desktop app, by default we do a soft redirect. The soft redirect we apply does not have a 301/307 status. Instead, the /index.html page is loaded and when the browser opens it, the redirect is made to the user's browser language or to the defaultLocale through the client JavaScript. To solve this, you must apply the redirects in the hosting where you host your web application. Here are some links to documentation that may help you depending on the hosting you use: Vercel redirects.\nAWS redirects.\nNetlify redirects",
- "titles": ["Static Site App"]
+ "titles": [
+ "Static Site App"
+ ]
},
{
"id": "/building-your-application/building/web-component-compiler#brisa-as-web-component-compiler",
@@ -1612,7 +2055,9 @@
"id": "/building-your-application/building/web-component-compiler#why-use-brisa-for-web-components",
"title": "Why Use Brisa for Web Components?",
"text": "Web Components are a powerful tool for building reusable UI elements that can be easily integrated into any web application, regardless of the framework or environment. However, creating and managing these components can be challenging, especially when considering server-side rendering (SSR), TypeScript support, and compatibility across different platforms. Brisa simplifies this process by providing a robust set of tools and commands that streamline the development and deployment of Web Components.",
- "titles": ["Brisa as Web Component Compiler"]
+ "titles": [
+ "Brisa as Web Component Compiler"
+ ]
},
{
"id": "/building-your-application/building/web-component-compiler#key-benefits-of-using-brisa-for-web-components",
@@ -1627,7 +2072,9 @@
"id": "/building-your-application/building/web-component-compiler#building-web-components-with-brisa",
"title": "Building Web Components with Brisa",
"text": "To create a Web Component using Brisa, you can utilize the brisa build -w command. This command compiles your Web Component into two distinct files: one for the client-side and one for the server-side. These files are optimized for different environments, ensuring that your Web Component performs well in both contexts. Command overview: brisa build -w path/to/your/web-component.tsx web-component.client.js: The client-side JavaScript file for your Web Component, optimized for browser environments.\nweb-component.server.js: The server-side JavaScript file for your Web Component, used for SSR with Declarative Shadow DOM. Example Output [ wait ] 🚀 building your standalone components...\n[ info ] Standalone components:\n[ info ] - build/custom-counter.server.js (646.00 B)\n[ info ] - build/custom-counter.client.js (425.00 B)\n[ info ] ✨ Done in 59.78ms.",
- "titles": ["Brisa as Web Component Compiler"]
+ "titles": [
+ "Brisa as Web Component Compiler"
+ ]
},
{
"id": "/building-your-application/building/web-component-compiler#using-the-web-component-outside-brisa",
@@ -1681,7 +2128,9 @@
"id": "/building-your-application/building/web-component-compiler#example",
"title": "Example",
"text": "You can use this example library to create your own web components with Brisa: Simple Counter as Web Component Example If you use the Brisa compiler to create your own web components, add this badge: \n \n Into your project's README file, let us know, and we will share your library on the Brisa website.",
- "titles": ["Brisa as Web Component Compiler"]
+ "titles": [
+ "Brisa as Web Component Compiler"
+ ]
},
{
"id": "/building-your-application/components-details/context#context",
@@ -1693,31 +2142,42 @@
"id": "/building-your-application/components-details/context#create-context-createcontext",
"title": "Create Context (createContext)",
"text": "createContext lets you create a context that components can provide or read. import { createContext } from \"brisa\";\n\nconst defaultValue = \"foo\";\nconst SomeContext = createContext(defaultValue); Parameters: defaultValue: The value that you want the context to have when there is no matching context provider in the tree above the component that reads context. If you don’t have any meaningful default value, specify null. The default value is meant as a “last resort” fallback. It is static and never changes over time.",
- "titles": ["Context"]
+ "titles": [
+ "Context"
+ ]
},
{
"id": "/building-your-application/components-details/context#provider",
"title": "Provider",
"text": "The context-provider component is required to propagate a value from this context to a sub-tree of components. The context-provider does not need any import. You can use the custom element context-provider by passing the context and value. It is a web component because this way the value is going to be shared with the web components and also you can use the same provider in web components. Server component: import { createContext } from \"brisa\";\nimport AnotherComponent from \"@/components/another-component\";\n\nconst ctx = createContext(\"foo\");\n\nexport default function ServerComponent() {\n \n \n ;\n} Web component: import { createContext } from \"brisa\";\n\nconst ctx = createContext(\"foo\");\n\nexport default function WebComponent() {\n \n \n ;\n} Parameters: SomeContext: The context that you’ve previously created with createContext. The context itself does not hold the information, it only represents the kind of information you can provide or read from components. Returns: useContext returns the context value inside a signal for the calling component. It is determined as the value passed to the closest context-provider above the calling component in the tree. If there is no such provider, then the returned value will be the defaultValue you have passed to createContext for that context. The returned value is up-to-date, reactive under a signal.",
- "titles": ["Context"]
+ "titles": [
+ "Context"
+ ]
},
{
"id": "/building-your-application/components-details/context#serveronly-property",
"title": "serverOnly property",
"text": "In many cases we want to share sensitive data on the server components-tree and that this data never reaches the client. To do this, the provider supports the serverOnly property and during SSR it is extripated so that it is never part of the final HTML. import { createContext } from \"brisa\";\nimport AnotherComponent from \"@/components/another-component\";\n\nconst ctx = createContext(\"foo\");\n\nexport default function ServerComponent() {\n \n \n ;\n} This means: \n
Hello
\n Is going to be transformed to just this HTML:
Hello
. Without the context-provider on top. The serverOnly property in runtime always works and there is no need to worry. However, if you don't use any client context and you don't want to carry the context-provider code in the client we recommend that you use literal values, like true|false directly, since during the build we don't evaluate if there are dynamic values, then in this case the provider code will be carried in the client even if it is not used later and you will never see any sensitive data.",
- "titles": ["Context", "Provider"]
+ "titles": [
+ "Context",
+ "Provider"
+ ]
},
{
"id": "/building-your-application/components-details/context#consume-context-usecontext",
"title": "Consume Context (useContext)",
"text": "useContext is a Brisa Hook that lets you read and subscribe to context from your component. export default function MyComponent(props, { useContext }) {\n const theme = useContext(ThemeContext);\n return
Hello world
useContext() call in a component is not affected by providers returned from the same component. The corresponding needs to be above the component doing the useContext() call.\nInstead of an import it is inside the RequestContext or WebContext. In the case of server the context is stored inside the request, since each request is different and it is better that it is not global to avoid concurrency problems. In the case of web is needed within the WebContext to generate a reactive signal that is cleared when the web component is disconnected.",
- "titles": ["Context"]
+ "titles": [
+ "Context"
+ ]
},
{
"id": "/building-your-application/components-details/context#when-to-use-context-instead-of-storebuilding-your-applicationcomponents-detailsweb-componentsstore-store-method",
"title": "When to use context instead of store",
"text": "Using Context instead of store comes at a price, since it generates a DOM element (context-provider web component) unless you have set serverOnly attribute. The difference is that store is a state shared with your entire app, while context is shared only between a tree of components. And the same context can have different values for different sub-trees. We recommend that you use store whenever possible. For specific cases that for example you have a list of components and you want to avoid prop-drilling and pass to each item its values through a context, then feel free to use context because this is its purpose. Example: import Ctx from \"@/some-context\";\n\nexport default function ItemListProvider({ items }) {\n return (\n
\n {items.map((item, index) => (\n \n {/* Avoid prop-drilling to the list-item component */}\n \n \n ))}\n
\n );\n}",
- "titles": ["Context"]
+ "titles": [
+ "Context"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#external-libraries",
@@ -1729,73 +2189,105 @@
"id": "/building-your-application/components-details/external-libraries#common-installation",
"title": "Common Installation",
"text": "For all types of libraries, including standalone, server components, and web components, you can use the bun install command to install them. This command fetches and installs the library, making it available for use throughout your project. bun install ",
- "titles": ["External Libraries"]
+ "titles": [
+ "External Libraries"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#standalone-libraries",
"title": "Standalone Libraries",
"text": "",
- "titles": ["External Libraries"]
+ "titles": [
+ "External Libraries"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#import-and-usage",
"title": "Import and Usage",
"text": "Once installed, you can import the library wherever needed in your project and use its functionalities accordingly. import myLibrary from \"\";\n\n// Use library functions or classes as needed,\n// whether inside web components, server\n// components, middleware, etc. This addition emphasizes the versatility of standalone libraries, making it clear that developers can utilize them in various parts of their project, depending on the specific needs of each component type.",
- "titles": ["External Libraries", "Standalone Libraries"]
+ "titles": [
+ "External Libraries",
+ "Standalone Libraries"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#server-components-dependencies",
"title": "Server Components Dependencies",
"text": "",
- "titles": ["External Libraries"]
+ "titles": [
+ "External Libraries"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#import-and-usage-1",
"title": "Import and Usage",
"text": "When dealing with server components, import the library and use its functionalities within your server-side code: import ServerComponent from \"\";\n\nexport default function MyPage() {\n // Use server components as needed inside the JSX\n return ;\n} While both server components and web components can leverage external libraries, the key distinction lies in how they are used. In server components, the integration is more straightforward, as you can directly import and use the libraries within JSX.",
- "titles": ["External Libraries", "Server Components Dependencies"]
+ "titles": [
+ "External Libraries",
+ "Server Components Dependencies"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#creating-a-server-component-library",
"title": "Creating a Server Component Library",
"text": "To create a server component library, you can use the brisa build -c component-path.tsx command. This command generates a new server component library, which you can then publish and install in your project. brisa build -c See the CLI documentation for more information on the brisa build command.",
- "titles": ["External Libraries", "Server Components Dependencies"]
+ "titles": [
+ "External Libraries",
+ "Server Components Dependencies"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#web-components-dependencies",
"title": "Web Components Dependencies",
"text": "",
- "titles": ["External Libraries"]
+ "titles": [
+ "External Libraries"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#import-and-usage-2",
"title": "Import and Usage",
"text": "To integrate a web component library, create a file named web-components/_integrations.(tsx|ts|js|jsx). Export an object where each key is the selector of the web component, and the corresponding value is the path (in string format) to the library, similar to what you would use in an import statement. import type { WebComponentIntegrations } from \"brisa\";\n\nexport default {\n \"custom-element\": \"\",\n // Add more mappings as needed\n} satisfies WebComponentIntegrations; Usage of web components within server components does not require additional imports. Simply include the web component tags directly in your server component code (or inside another web component): // Inside a server component or inside another web-component\n
\n \n
This approach allows seamless integration of web components within your server components, maintaining a clean and concise code structure. Note for Developers: The web components specified in web-components/_integrations will be dynamically included in the client-side code only when they are used on a particular page.",
- "titles": ["External Libraries", "Web Components Dependencies"]
+ "titles": [
+ "External Libraries",
+ "Web Components Dependencies"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#ssr-of-external-web-components",
"title": "SSR of external Web Components",
"text": "Using this declaration: import type { WebComponentIntegrations } from \"brisa\";\n\nexport default {\n \"custom-element\": \"\",\n // Add more mappings as needed\n} satisfies WebComponentIntegrations; It only do SSR in the case of Brisa web components without any transpilation, not in the case of native web components or transpiled Brisa web components. However, there is a solution. Brisa is more than a framework, it is also a tool to create Web Component libraries, so all web components created with this Brisa tool can be used in any framework or VanillaJS, and apart, you can use the file with the suffix .server.js to do SSR of the web components. So all web components made with Brisa can be imported with this other declaration: import type { WebComponentIntegrations } from \"brisa\";\n\nexport default {\n \"custom-element\": {\n client: \".client\",\n server: \".server\",\n types: \".types\",\n },\n // Add more mappings as needed\n} satisfies WebComponentIntegrations; If your favorite Web Component library is not compatible with SSR, tell them to contact us so they can make it compatible, we want the web to be a better and more accessible place for everyone so we are willing to help any library to be compatible with Brisa.",
- "titles": ["External Libraries", "Web Components Dependencies"]
+ "titles": [
+ "External Libraries",
+ "Web Components Dependencies"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#typescript-types",
"title": "TypeScript Types",
"text": "Using this declaration: import type { WebComponentIntegrations } from \"brisa\";\n\nexport default {\n \"custom-element\": '',\n // Add more mappings as needed\n} satisfies WebComponentIntegrations; It only use types when is a Brisa Web Component without transpilation and has .ts extension. If it is a native web component or a Brisa Web Component with transpilation, you have to create a .d.ts file with the types of the web component. In order to specify the types you need to change the declaration to: import type { WebComponentIntegrations } from \"brisa\";\n\nexport default {\n \"custom-element\": {\n client: \".client\",\n types: \".types.d.ts\",\n },\n // Add more mappings as needed\n} satisfies WebComponentIntegrations; And create a file with the name .types.d.ts with the types of the web component. Example of types for a web component: export default function CustomCounter({ start }: { start?: number }): JSX.Element; Note for library creators: Use an export default in the .d.ts file to export the types of the web component.",
- "titles": ["External Libraries", "Web Components Dependencies"]
+ "titles": [
+ "External Libraries",
+ "Web Components Dependencies"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#creating-a-web-component-library",
"title": "Creating a Web Component Library",
"text": "To create a server component library, you can use the brisa build -w web-component-path.tsx command. This command generates a new server component library, which you can then publish and install in your project. brisa build -w This is going to create two files: web-component-path.client.js and web-component-path.server.js. The first one is for the client-side, and the second one is for the server-side (SSR with Declarative Shadow DOM). You can use the web component in any framework or VanillaJS. See the CLI documentation for more information on the brisa build command.",
- "titles": ["External Libraries", "Web Components Dependencies"]
+ "titles": [
+ "External Libraries",
+ "Web Components Dependencies"
+ ]
},
{
"id": "/building-your-application/components-details/external-libraries#third-party-brisa-web-components-vs-native-web-components",
"title": "Third party Brisa web components vs native web components",
"text": "For both, you can use the web-components/_integrations.(tsx|ts|js|jsx): export default {\n \"brisa-element\": \"\",\n \"native-web-component\": {\n client: \".client\",\n server: \".server\",\n types: \".types\",\n },\n}; As a note for those who create open-source libraries, the difference is that if there is a default export, it interprets it as a Brisa web component, so you can create any Brisa library with it: export default function BrisaElement() {\n return
My Brisa Element
;\n} So then, you don't need any transpilation and with only this declaration: \"brisa-element\": \"\", it works for SSR and types. However, you can use transpilation to allow the same code to be used in any framework or VanillaJS. Doing this, it's converted to native web components, so then you need to load the .client.js file in the client-side and the .server.js file in the server-side: \"brisa-element\": {\n client: \".client\",\n server: \".server\",\n types: \".types\",\n}, In the case you want to create a native web component, you don't need to export it, and you can use the customElements.define method: customElements.define(\n \"native-web-component\",\n class extends HTMLElement {\n connectedCallback() {\n this.innerHTML = \"My Native Web Component\";\n }\n },\n); About the definition of the name: Be careful that the selector name inside customElements.define should be the same as the one in the web-components/_integrations file.",
- "titles": ["External Libraries", "Web Components Dependencies"]
+ "titles": [
+ "External Libraries",
+ "Web Components Dependencies"
+ ]
},
{
"id": "/building-your-application/components-details/forms#forms",
@@ -1807,49 +2299,70 @@
"id": "/building-your-application/components-details/forms#uncontrolled-forms-recommended",
"title": "Uncontrolled Forms (recommended 👌)",
"text": "",
- "titles": ["Forms"]
+ "titles": [
+ "Forms"
+ ]
},
{
"id": "/building-your-application/components-details/forms#server-component-recommended",
"title": "Server Component (recommended 👌)",
"text": "An uncontrolled form is a form where the values are not bound to the component's state, allowing for a more straightforward and less verbose approach. Uncontrolled forms are useful in scenarios where the form is relatively simple, and the overhead of managing form state through the component is unnecessary. Uncontrolled forms can be created in both web components and server components. The browser events in Brisa can be handled in the server. In fact, we recommend that if you use uncontrolled forms, use a server component. This way: You make the app lighter, less JS code to the client.\nYou can handle directly the form on the server. export default function UncontrolledFormServer() {\n return (\n \n );\n} The onSubmit on the server works like the submit event (triggered by the client) merged with the formdata event (is fired after the FormData invocation). The difference with the client onSubmit are: The e.preventDefault() is always done automatically in the server actions.\nThe FormData is built to send and process it from the server, modifying the event from onSubmitEvent to FormDataEvent.\nSince the event is FormDataEvent, you can access the form data directly through e.formData.\ne.target.reset() and e.currentTarget.reset() instead of being executed right away, they are executed when the server action ends. In fact, it is now even easier to deal with form server interactions from the server than with the client.",
- "titles": ["Forms", "Uncontrolled Forms (recommended 👌)"]
+ "titles": [
+ "Forms",
+ "Uncontrolled Forms (recommended 👌)"
+ ]
},
{
"id": "/building-your-application/components-details/forms#reset-form",
"title": "Reset form",
"text": "The browser events inside server actions are serialized. However, e.target.reset() and e.currentTarget.reset() still works in the server. The only difference is that it is not executed at the right time, but is marked to reset it when the server action is finished and returns the response to the client. export default function UncontrolledFormServer() {\n return (\n \n );\n} Adds 0 bytes of JS client. Only the Brisa RPC client is needed which is ~2kB.",
- "titles": ["Forms", "Uncontrolled Forms (recommended 👌)"]
+ "titles": [
+ "Forms",
+ "Uncontrolled Forms (recommended 👌)"
+ ]
},
{
"id": "/building-your-application/components-details/forms#web-component-not-recomended",
"title": "Web component (not recomended 👎)",
"text": "The client code of a uncontrolled form would be as follows: export default function UncontrolledFormClient() {\n return (\n \n );\n} If you are making an uncontrolled form in a web component it is a sign that you are writing code on the client that could be written on the server. Use only uncontrolled form in web components only if you don't need to make a request to the server after the submit. Otherwise use a server component. Using uncontrolled form in web components adds client JS code, not only the event JS, but also the JS of the web component itself.",
- "titles": ["Forms", "Uncontrolled Forms (recommended 👌)"]
+ "titles": [
+ "Forms",
+ "Uncontrolled Forms (recommended 👌)"
+ ]
},
{
"id": "/building-your-application/components-details/forms#controlled-forms-not-recomended",
"title": "Controlled Forms (not recomended 👎)",
"text": "A controlled form in Brisa is a form whose state is controlled by the Brisa web component. In other words, the form elements such as input fields, checkbox, radio buttons, etc have their values bound to the component's state. This allows to manage and control the form's behavior and be able to give instant feedback to the user about errors. Despite the benefits of controlled forms, it's important to note that Brisa doesn't necessarily recommend their use in all scenarios. The decision to opt for controlled or uncontrolled forms should be driven by specific requirements and architectural considerations. In certain cases, controlled forms may introduce unnecessary complexity, especially when dealing with large forms or integrating with external libraries. Developers should carefully assess the trade-offs and choose the approach that aligns best with their application's needs. import { type WebContext } from \"brisa\";\n\nexport default function ControlledFormExample({}, { state }: WebContext) {\n const username = state(\"\");\n\n return (\n \n );\n} If you want to use controlled forms, we recommend that you use it in the web components and not in the server components. If you want to manage the onSubmit for the server, you can manage it through a prop and the parent server component capture and handle the event. import { type WebContext } from \"brisa\";\n\n// Web component:\nexport default function ControlledFormExample(\n { onFormSubmit }, // Event captured and handled by server component\n { state }: WebContext,\n) {\n const username = state(\"\");\n\n return (\n \n );\n} And: // Server component:\nexport default function Page() {\n const onFormSubmit = ({ username }) => {\n // This event is handled in the server, we can save it to the DB.\n };\n\n return ;\n} Controlled forms introduce additional complexity and more client-side JavaScript code. Developers should carefully weigh these factors when choosing between controlled and uncontrolled forms. The Brisa team recommends using controlled forms primarily when providing instant feedback to users for each modification during form interactions. Otherwise, it is advisable to opt for uncontrolled forms with a server component.",
- "titles": ["Forms"]
+ "titles": [
+ "Forms"
+ ]
},
{
"id": "/building-your-application/components-details/forms#mixing-controlled-and-uncontrolled-fields",
"title": "Mixing controlled and uncontrolled fields",
"text": "In scenarios where a form is implemented within a server component due to the static nature of most fields, an exception arises when a dynamic field is included through a Web Component. Since Web Components use a separate Shadow DOM, the value of this dynamic field, encapsulated within the Shadow DOM, is not automatically included when capturing FormData from the parent form.",
- "titles": ["Forms"]
+ "titles": [
+ "Forms"
+ ]
},
{
"id": "/building-your-application/components-details/forms#associated-web-component",
"title": "Associated Web Component",
"text": "To address this issue, the ElementInternals API must be employed within Brisa to ensure that the Web Component is properly integrated as a form field. This approach allows the Web Component to be recognized as part of the form, ensuring that its value is included when FormData is collected from the form. Example: import type { WebContext } from \"brisa\";\n\nexport default function SomeDynamicInput({ }, { self }: WebContext) {\n const internals = self.attachInternals();\n\n function onInput(e) {\n internals.setFormValue(e.target.value);\n }\n\n return \n} Doing internals.setFormValue(e.target.value) we are setting the value of the Web Component to the form field. This way, the value of the dynamic field will be included when capturing FormData from the parent form. self.attachInternals() can be used without an effect, it is supported on SSR without any problem. Think when you are using self.attachInternals() you are extending a default Element Internals class. And therefore default values are set. For example, if you try to use an input type=\"email\" or required, no actions will happen from the browser unless you define Validity. import type { WebContext } from \"brisa\";\n\nexport default function WebComponent({ }, { self }: WebContext) {\n const internals = self.attachInternals();\n\n return (\n {\n const input = e.currentTarget;\n // Updates the validation of the internal element (input)\n internals.setValidity(input.validity, input.validationMessage);\n console.log('isValid', internals.checkValidity());\n }}\n />\n );\n} The ValidityState object is used to represent the validity states of an element. It contains properties that indicate whether the element meets its validation criteria, and the reason why it doesn't.",
- "titles": ["Forms", "Mixing controlled and uncontrolled fields"]
+ "titles": [
+ "Forms",
+ "Mixing controlled and uncontrolled fields"
+ ]
},
{
"id": "/building-your-application/components-details/forms#usage-in-a-form",
"title": "Usage in a Form",
"text": "After attaching the Web Component to the form, the value of the dynamic field will be included when capturing FormData from the parent form. Example of usage on a Server Component: export default function Page() {\n return (\n \n );\n} The name attribute is required in the Web Component to be recognized as a form field. For more information on the ElementInternals API, take a look these docs:\n\nElementInternals MDN Web Docs\nForm-associated custom elements\nElementInternals and Form-Associated Custom Elements",
- "titles": ["Forms", "Mixing controlled and uncontrolled fields"]
+ "titles": [
+ "Forms",
+ "Mixing controlled and uncontrolled fields"
+ ]
},
{
"id": "/building-your-application/components-details/reactivity#reactivity",
@@ -1861,25 +2374,33 @@
"id": "/building-your-application/components-details/reactivity#are-signals-readonly",
"title": "Are signals readonly?",
"text": "Only the props signals are readonly, otherwise are writable. However, you can't mutate them directly. You have to use the .value clause to mutate them setting a new value. For example: count.value = 10; // ✅\ncount = 10; // ❌\n\nuser.value = { username: \"Aral\" }; // ✅\nuser = { username: \"Aral\" }; // ❌\nuser.value.username = \"Aral\"; // ❌ To update the value of a signal, you need to provoke a setter to the value property. For example: user.value = { username: 'Aral' };. If you try to update the inner properties of the signal directly, the reactivity will not work, because doing user.value.username = 'Aral' will trigger a getter (user.value.username) and not a setter.",
- "titles": ["Reactivity"]
+ "titles": [
+ "Reactivity"
+ ]
},
{
"id": "/building-your-application/components-details/reactivity#are-props-reactive",
"title": "Are props reactive?",
"text": "Props are an special kind of signals optimized in compilation-time. You don't need to use the .value clause to consume them. They are readonly. So, this means that you can't mutate them. This also allows to define a default value for a prop in a easy way: export default function Counter({ initialValue = 0 }, { state }: WebContext) {\n const count = state(initialValue);\n\n return (\n <>\n \n Counter: {count.value} \n \n >\n );\n} In Brisa we are doing optimizations in build-time to allow you to declare props inside the component arguments without losing reactivity. For example username and displayName are reactive in the following component: export default function UserCard({\n user: { username, displayName } = { username: \"Unknown\" },\n}) {\n return (\n <>\n Username: {username} \n Display Name: {displayName} \n >\n );\n} An alternative way to do it outside the component arguments is consuming directly the signal inside the JSX: export default function UserCard({ user }) {\n return (\n <>\n Username: {user.username ?? \"Unknown\"} \n Display Name: {user.displayName} \n >\n );\n} However, this way is not recommended because it breaks the reactivity: export default function Counter({ user }) {\n const { username, displayName } = user;\n\n return (\n <>\n Username: {username} \n Display Name: {displayName} \n >\n );\n} For derived props, you can use the derived method: export default function Counter({ user }, { derived }: WebContext) {\n const username = derived(() => user.username.toUpperCase());\n const displayName = derived(() => user.displayName.toUpperCase());\n\n return (\n <>\n Username: {username.value} \n Display Name: {displayName.value} \n >\n );\n} In the last example, username and displayName are derived signals from the user prop signal to display the username and display name in uppercase.",
- "titles": ["Reactivity"]
+ "titles": [
+ "Reactivity"
+ ]
},
{
"id": "/building-your-application/components-details/reactivity#can-i-create-a-signal-from-a-signal",
"title": "Can I create a signal from a signal?",
"text": "Yes, you can create a signal from a signal using the derived method. This method is useful to create a signal that depends on other signals. export default function DoubleCounter({}, { state, derived }: WebContext) {\n const count = state(0);\n const double = derived(() => count.value * 2);\n\n return (\n
\n \n {double.value}\n \n
\n );\n} In this example, double is a signal that depends on count. When count changes, double will automatically update. If you want to create a custom signal inside the WebContext to re-use it in multiple components, you can use expand the WebContext.",
- "titles": ["Reactivity"]
+ "titles": [
+ "Reactivity"
+ ]
},
{
"id": "/building-your-application/components-details/reactivity#can-i-use-signals-in-the-server",
"title": "Can I use signals in the server?",
"text": "No directly. The signals are reactive and they are used in the client-side. However, action-signals concept exists and you can use the server store method and transfer some store fields to the client-side and reactively update them on a server action. import type { RequestContext } from \"brisa\";\nimport { rerenderInAction } from \"brisa/server\";\n\nexport default function ServerComponent({}, { store }: RequestContext) {\n // Setting store\n if (!store.has(\"user\")) {\n store.set(\"user\", { username: \"foo\", displayName: \"Foo\" });\n }\n\n // Extends the life of the store property beyond request-time\n // (Necessary to use it in a server action)\n store.transferToClient([\"user\"]);\n\n function updateName() {\n // Update the store inside a server action\n store.set(\"user\", { username: \"bar\", displayName: \"Bar\" });\n rerenderInAction({ type: \"page\" });\n }\n\n // Consuming store\n return (\n <>\n Hello {store.get(\"user\").displayName}\n \n >\n );\n} The natural lifecycle of the server store is: Middleware\nRendering (layout, page, components) But thanks to store.transferToClient method, you extend the lifecycle of some store fields to: Middleware\nRendering (layout, page, components)\nClient side (as signal)\nServer Actions\nClient side (as signal)\netc ... And modifing the value on server actions, is reflected in a reactive way on the client side signals.",
- "titles": ["Reactivity"]
+ "titles": [
+ "Reactivity"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#server-components",
@@ -1891,97 +2412,134 @@
"id": "/building-your-application/components-details/server-components#differences-with-web-components",
"title": "Differences with Web Components",
"text": "Although the server components are interactive in Brisa, the interactivity of the server components is more focused on interactivity where the server is involved. It would not make sense to do the interactivity on the server for Spreadsheet cell components, since all this interactivity could be on the client avoiding constant calls to the server. Web components are fully reactive thanks to signals, and you can access the Web API.",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#creating-a-server-component",
"title": "Creating a Server Component",
"text": "Server Components can be created anywhere except inside the web-components folder. Example: .\n└── components\n └── server-component.tsx And it has to be a JSX component: export default function ServerComponent({ children }) {\n return (\n \n
Hello World
\n {children}\n \n );\n}",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#props",
"title": "Props",
"text": "Brisa components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions (server actions).",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#step-1-pass-props-to-the-child-component",
"title": "Step 1: Pass props to the child component",
"text": "First, pass some props to user-images. For example, let’s pass three props: urls (array of strings), width and height (number): src/components/user-info.tsx: import UserImages from \"@/components/user-images\";\n\nexport default function UserInfo() {\n return (\n \n );\n} Now you can read these props inside the UserImages component.",
- "titles": ["Server Components", "Props"]
+ "titles": [
+ "Server Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#step-2-read-props-inside-the-child-component",
"title": "Step 2: Read props inside the child component",
"text": "You can read these props by listing their names urls, width, height separated by the commas inside ({ and }) directly after function UserImages. This lets you use them inside the UserImages code, like you would with a variable. src/components/user-images.tsx: export default function UserImages({ urls, width, height }) {\n // urls, width and height are available here\n return urls.map((imageUrl) => (\n \n ));\n}",
- "titles": ["Server Components", "Props"]
+ "titles": [
+ "Server Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#specifying-a-default-value-for-a-prop",
"title": "Specifying a default value for a prop",
"text": "If you want to give a prop a default value to fall back on when no value is specified, you can do it with the destructuring by putting = and the default value right after the parameter: export default function UserImages({ urls = [], width = 300, height = 300 }) {\n // ...\n}",
- "titles": ["Server Components", "Props"]
+ "titles": [
+ "Server Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#key-property",
"title": "key property",
"text": "Each child in a list should have a unique \"key\" prop. Keys tell Brisa which array item each component corresponds to, so that it can match them up later after the execution of a server action that does a rerenderInAction. This becomes important if your array items can move (e.g. due to sorting), get inserted, or get deleted. A well-chosen key helps Brisa infer what exactly has happened, and make the correct updates to the DOM tree after the server action execution. export default function List({ people }) {\n return (\n
\n {people.map((person) => (\n
\n \n
\n {person.name}\n {\" \" + person.profession + \" \"}\n known for {person.accomplishment}\n
\n
\n ))}\n
\n );\n} The key property is also an attribute to identify the instance of a web component. When the value of key changes, it forces a unmount + mount of the web component again, resetting the state of the component. Therefore, from the rendering of a server action executed in a Server Component you can control whether to reset the internal state of a web component (by changing the key) or keep it (without changing the key). export default function ExampleOfKey({}, { i18n }) {\n // If locale change, some-component is going to be unmounted + mounted\n return ;\n}",
- "titles": ["Server Components", "Props"]
+ "titles": [
+ "Server Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#children",
"title": "Children",
"text": "In Brisa, the children prop is a special prop that allows components to pass elements, components, or even plain text as children to another component. It provides a flexible way to compose and structure Brisa applications. The children prop is implicitly available and can be accessed as an argument. Let's take a look at a simple example: src/components/my-component.tsx: const MyComponent = ({ children }) => {\n return (\n
\n \n );\n};\n\nexport default ParentComponent; In this example, the MyComponent component can render its content and any child components passed to it using the children prop.",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#events-server-actions",
"title": "Events (server actions)",
"text": "In Brisa Components there is a convention that events must start with on prefix. Ex: onNext, onPrev. After following this convention you can use events in Server Components like browser events, with some differences: The browser event is serialized\nThe code is executed on the server\nIt's not adding JS client code src/components/password.tsx: export default function Password({ onValidatePassword }) {\n return (\n
\n {\n console.log(\"this code is executed on the server!\");\n // Send data to another server action:\n onValidatePassword(e.target.value);\n }}\n // Debounce the browser event 300ms\n debounceInput={300}\n />\n
\n );\n} Consuming the onValidatePassword server action of this Server Component from another component: src/components/example.tsx: import { navigate } from \"brisa\";\n\nexport default function Example() {\n function validatePassword(password) {\n if (password === \"foo\") navigate(\"/bar\");\n }\n\n return ;\n} For more information about Server Actions, read the documentation about Server Actions.",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#store-store-method",
"title": "Store (store method)",
"text": "The store is a shared state among all server-components (possible to transfer some store values on web-components also). The store is a Map that lives at request time (inside the request).",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#example",
"title": "Example:",
"text": "src/components/shared-store.tsx: import type { RequestContext } from \"brisa\";\nimport { rerenderInAction } from \"brisa/server\";\n\nexport default function SharedStore({}, { store }: RequestContext) {\n // Setting store\n if (!store.has(\"user\")) {\n store.set(\"user\", { username: \"foo\", displayName: \"Foo\" });\n }\n\n // Extends the life of the store property beyond request-time\n // (Necessary to use it in a server action)\n store.transferToClient([\"user\"]);\n\n function updateName() {\n // Update the store inside a server action\n store.set(\"user\", { username: \"bar\", displayName: \"Bar\" });\n rerenderInAction({ type: \"page\" });\n }\n\n // Consuming store\n return (\n <>\n Hello {store.get(\"user\").displayName}\n \n >\n );\n} For more information about store, read the documentation about RequestContext.",
- "titles": ["Server Components", "Store (store method)"]
+ "titles": [
+ "Server Components",
+ "Store (store method)"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#context",
"title": "Context",
"text": "To share context between Server Components without prop drilling you can use context. To use context take a look to: createContext\ncontext-provider\nuseContext Example parent: import { createContext } from \"brisa\";\n\nconst ctx = createContext({});\n\nexport default function ThemeProvider({ color, children }) {\n \n {children}\n ;\n} Example sub-tree child component: export default function SomeChildComponent(props, { useContext }) {\n const theme = useContext(ThemeContext);\n return
Hello world
Learn more about it here.",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#template-literal-css",
"title": "Template literal css",
"text": "You can write CSS in your components using the template literal named css. The return value of css is nothing. As it runs, the css is injected during the render. import { RequestContext } from \"brisa\";\n\nexport default function Component(\n { color }: { color: string },\n { css }: RequestContext,\n) {\n css`\n .color {\n color: ${color};\n }\n `;\n\n return
{color}
;\n} You can run this literal template several times and the styles will accumulate.",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#inject-html-dangerhtml",
"title": "Inject HTML (dangerHTML)",
"text": "Make situations that we want to inject HTML that we have in string to the DOM. For these occasions, you can use the dangerHTML function. Since without this function it is escaped by security. import { dangerHTML } from \"brisa\";\n\nexport default function SomeComponent() {\n return (\n <>\n {/* Escaped by default (doesn't work for security): */}\n {''}\n\n {/* Force to inject an string as HTML: */}\n {dangerHTML(\n '',\n )}\n >\n );\n}",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#suspense-component-phase",
"title": "Suspense component phase",
"text": "You can generate a suspense phase if your component is async and you want to show something while the promise is pending. It also works during HTML streaming. export default async function MyServerComponent({}, { state }) {\n const foo = await fetch(/* ... */).then((r) => r.text());\n\n return
; See more details here to learn more.",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/server-components#handle-component-error",
"title": "Handle component error",
"text": "You can generate a error phase if your server component throws an error and you want to show something without crash the rest of the page. import { RequestContext } from \"brisa\";\n\nexport default function SomeComponent() {\n /* some code */\n}\n\nSomeComponent.error = ({ error, ...props }, requestContext: RequestContext) => {\n return
Oops! {error.message}
;\n}; See more details here to learn more.",
- "titles": ["Server Components"]
+ "titles": [
+ "Server Components"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#web-components",
@@ -1993,247 +2551,346 @@
"id": "/building-your-application/components-details/web-components#differences-with-server-components",
"title": "Differences with Server Components",
"text": "In order to make it easy during development, we support the fact that creating web-components is very similar to the rest of the components (Brisa mode). However there are some differences. Although the server components are interactive in Brisa, the interactivity of the server components is more focused on interactivity where the server is involved. It would not make sense to do the interactivity on the server for Spreadsheet cell components, since all this interactivity could be on the client avoiding constant calls to the server. Web components are fully reactive thanks to signals. There are no rerenders, no virtual DOM and no reconciliation. And of course, unlike Server Components, you can access the Web API: import { WebContext } from \"brisa\";\n\nexport default function SomeWebComponent({}, { onMount, cleanup }: WebContext) {\n onMount(() => document.addEventListener(\"scroll\", onScroll));\n cleanup(() => document.removeEventListener(\"scroll\", onScroll));\n\n function onScroll(event) {\n // some implementation of the scroll event\n }\n\n return
{/* some content */}
;\n} In the last example, the scroll event is recorded when the web component is mounted and deleted when the web component is unmounted.",
- "titles": ["Web Components"]
+ "titles": [
+ "Web Components"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#creating-a-web-component",
"title": "Creating a Web Component",
"text": "",
- "titles": ["Web Components"]
+ "titles": [
+ "Web Components"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#step-1-create-the-file",
"title": "Step 1: Create the file",
"text": "Unlike Server Components that can be created anywhere, web components must be inside the src/web-component folder. For example, let's create this file: src/web-component/some-example.tsx. Here the file name is very important, since it will be the name of the web-component selector. That is, we will be able to use it in other web/server components with the tag: Some example The names of the Web Components, as a convention, must be created in kebab-case and at least 2 batches to avoid conflicts with other elements of the web. However, you can group them in folders. Route\nCorrect\nselector\n\n\n\n\nsrc/web-component/some-example.tsx\n✅\n\n\n\nsrc/web-component/some/example.tsx\n✅\n\n\n\nsrc/web-component/some-complex-example.tsx\n✅\n\n\n\nsrc/web-component/SomeExample.tsx\n❌\n-\n\n\nsrc/web-component/someExample.tsx\n❌\n-\n\n\nsrc/web-component/some_complex-example.tsx\n❌\n-\n\n\nsrc/web-component/some_complex/example.tsx\n❌\n-\n\n\nsrc/web-component/some.tsx\n❌\n-",
- "titles": ["Web Components", "Creating a Web Component"]
+ "titles": [
+ "Web Components",
+ "Creating a Web Component"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#step-2-write-and-export-default-the-component",
"title": "Step 2: Write and export default the component",
"text": "Once we have created the file, we can write our Web Component. The only thing we need to do to make it available is to make a export default of the component. export default function HelloWorld() {\n return
Hello World
;\n}",
- "titles": ["Web Components", "Creating a Web Component"]
+ "titles": [
+ "Web Components",
+ "Creating a Web Component"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#props",
"title": "Props",
"text": "Brisa components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions. The properties are signals but can be used directly without using the .value, because they are readonly. Good to know: Since props are signals, consume them directly or use derived method. Doing so breaks the reactivity:\nexport default function UserImages({ urls }, { derived }) {\n // ❌ variable is no longer reactive\n const firstImage = urls[0];\n // ✅ Instead, use derived:\n const reactiveFirstImage = derived(() => urls[0]);\n}\n\nIn Brisa we are doing optimizations in build-time to avoid losing the reactivity of the props declared on the parameters. So the last example can be solved without the derived method with these props declaration:\nexport default function UserImages({ urls: [firstImage] }) {\n // firstImage is reactive\n}\n\nHowever, it is important to know that if you declare a variable with the prop, it will not be reactive. Avoid spreading props without specify the prop field:\nexport default function UserImages(props) {\n // ❌ Brisa can't know what is the prop field to observe\n return ;\n}\n\nThis is not related with reacitivity, but for web components Brisa needs to specify the observedAttributes.",
- "titles": ["Web Components"]
+ "titles": [
+ "Web Components"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#step-1-pass-props-to-the-child-component",
"title": "Step 1: Pass props to the child component",
"text": "First, pass some props to user-images. For example, let’s pass three props: urls (array of strings), width and height (number): src/web-components/user-info.tsx: export default function UserInfo() {\n return (\n \n );\n} Now you can read these props inside the user-images component.",
- "titles": ["Web Components", "Props"]
+ "titles": [
+ "Web Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#step-2-read-props-inside-the-child-component",
"title": "Step 2: Read props inside the child component",
"text": "You can read these props by listing their names urls, width, height separated by the commas inside ({ and }) directly after function UserImages. This lets you use them inside the UserImages code, like you would with a variable. src/web-components/user-images.tsx: export default function UserImages({ urls, width, height }) {\n // urls, width and height are available here\n return urls.map((imageUrl) => (\n \n ));\n}",
- "titles": ["Web Components", "Props"]
+ "titles": [
+ "Web Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#specifying-a-default-value-for-a-prop",
"title": "Specifying a default value for a prop",
"text": "If you want to give a prop a default value to fall back on when no value is specified, you can do it with the destructuring by putting = and the default value right after the parameter: export default function UserImages({ urls = [], width = 300, height = 300 }) {\n // ...\n} Adding defaults in this way does not break reactivity.",
- "titles": ["Web Components", "Props"]
+ "titles": [
+ "Web Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#key-property",
"title": "key property",
"text": "Each child in a list should have a unique \"key\" prop. Keys tell Brisa which array item each component corresponds to, so that it can match them up later. This becomes important if your array items can move (e.g. due to sorting), get inserted, or get deleted. A well-chosen key helps Brisa infer what exactly has happened, and make the correct updates to the DOM tree. export default function List({ people }) {\n return (\n
\n {people.map((person) => (\n
\n \n
\n {person.name}\n {\" \" + person.profession + \" \"}\n known for {person.accomplishment}\n
\n
\n ))}\n
\n );\n} The key property is also an attribute to identify the instance of a component. When the value of key changes, it forces a unmount + mount of the component again, resetting the state of the component. For example, if we use the locale as key, changing the locale will reset the state of the component. export default function ExampleOfKey({}, { i18n }) {\n // If locale change, some-component is going to be unmounted + mounted\n return ;\n}",
- "titles": ["Web Components", "Props"]
+ "titles": [
+ "Web Components",
+ "Props"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#children",
"title": "Children",
"text": "In Brisa, the children prop is a special prop that allows components to pass elements, components, or even plain text as children to another component. It provides a flexible way to compose and structure Brisa applications. The children prop is implicitly available and can be accessed as an argument. Let's take a look at a simple example: src/web-components/my-component.tsx: const MyComponent = ({ children }) => {\n return (\n
\n \n );\n};\n\nexport default ParentComponent; In this example, the my-component component can render its content and any child components passed to it using the children prop.",
- "titles": ["Web Components"]
+ "titles": [
+ "Web Components"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#slots",
"title": "Slots",
"text": "However, Web-components, by their nature, have the possibility to use the slot tag also to define children. You can use it in case you need more than one child. The prop children is an equivalent to an unnamed slot. This is also valid: src/web-components/my-component-using-slots.tsx: const MyComponentUsingSlots = () => {\n return (\n
\n \n );\n};\n\nexport default ParentComponentUsingSlots; Good to know: Slots only work in Web Components. In Server Components only works children prop.",
- "titles": ["Web Components", "Children"]
+ "titles": [
+ "Web Components",
+ "Children"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#props-as-jsx",
"title": "Props as JSX",
"text": "You can pass JSX elements as props to a component: export default function Home() {\n return Server Title} /> // ✅\n} Using Server components as props is also valid: export default function Home() {\n return } /> // ✅\n} It's just an alternative to slots and children, but slots and children have better performance because it is not necessary to deserialize and render inside the Web Component.",
- "titles": ["Web Components", "Children"]
+ "titles": [
+ "Web Components",
+ "Children"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#events",
"title": "Events",
"text": "",
- "titles": ["Web Components"]
+ "titles": [
+ "Web Components"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#events-via-attributes",
"title": "Events via attributes",
"text": "In Brisa Components there is a convention that events must start with on prefix. Ex: onNext, onPrev. This convention is because it is necessary to distinguish between attributes that are events and those that are not. And as the functions cannot serialize, we came to make this convention. After following this convention you can use events in Web Components as if you were in other frameworks such as React. src/web-components/color-selector.tsx: import { WebContext } from \"brisa\";\n\nexport default function ColorSelector({ color, onChangeColor }) {\n return (\n
\n );\n} Consuming the event onChangeColor of this Web Component from another component: src/web-components/color-circle.tsx: import { WebContext } from \"brisa\";\n\nexport default function ColorSVG({}: any, { state }: WebContext) {\n const color = state(\"#ff0000\");\n\n return (\n <>\n Select a color: \n (color.value = newColor)}\n />\n\n \n >\n );\n} Good to know: Brisa internally uses CustomEvent to communicate between Web Components. It is also used for communication between Web Components and Server Components via Server Actions.\nAll event listeners are automatically added and cleaned up by Brisa.",
- "titles": ["Web Components", "Events"]
+ "titles": [
+ "Web Components",
+ "Events"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#dom-events",
"title": "DOM events",
"text": "As web components are DOM elements, they also automatically have their own events. You can capture an onClick of any Web Component without the need to implement it inside: console.log(\"onClick event already work? 🤯\", e)}\n onChangeColor={(newColor: string) => (color.value = newColor)}\n/> Good to know: It is important to know this when naming events that do not conflict with existing DOM events, to avoid \"event fires twice\" issues. Also important if you want to overwrite a DOM event, use the e.stopPropagation() to avoid the conflict.",
- "titles": ["Web Components", "Events"]
+ "titles": [
+ "Web Components",
+ "Events"
+ ]
},
{
"id": "/building-your-application/components-details/web-components#events-on-ref",
"title": "Events on ref",
"text": "You can register an event after accessing an element with the ref attribute and state: export default ({}, { onMount, cleanup, state }: WebContext) => {\n const ref = state(null);\n\n function onClick(e) {\n console.log(\"Event via ref\", e);\n }\n\n onMount(() => ref.value.addEventListener(\"click\", onClick));\n cleanup(() => ref.value.removeEventListener(\"click\", onClick));\n\n return