I read in the documentation about how to use Endpoints to send json
, to send images, for redirects, for form validation, but I want to send html
.
I want to use Astro
together with htmx
and am trying to use server to send small snippets of html
. Here's what I got:
Tha backend part:
// clicked.ts file
import type { APIContext } from 'astro';
export async function post({ cookies, params, request }: APIContext) {
return new Response(
`<button hx-post="/clicked" hx-swap="outerHTML">
Click me again
</button>`,
{ status: 200, headers: { 'Content-Type': 'text/html' } }
);
}
and the frontend part:
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me
</button>
But I don't like the ugly look of sending html
as a string, would like to reuse Astro
templates.
Using Astro's server-side rendering capabilities, we can implement this desired functionality without the need to create an additional endpoint. We can use the front-matter of an .astro
page to accomplish this.
First, make sure your astro.config.mjs
includes output: "hybrid"
, like so:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
output: "hybrid",
});
Then, create the src/pages/clicked.astro
file:
---
// src/pages/clicked.astro
export const partial = true
export const prerender = false
---
<button hx-post="/clicked" hx-swap="outerHTML">
Click me again
</button>
Your initial "Click Me" button should now work!
Moreover, here's a better example of a comprehensive implementation for a server-side rendered partial page, directly from within an .astro
file - ready to use with HTMX.
--
// src/pages/chat/gpt_response.astro
import ChatMessage from "../../components/chat/ChatMessage.astro"
import OpenAI from "openai"
export const partial = true
export const prerender = false
const openai = new OpenAI({ apiKey: import.meta.env.OPENAI_API_KEY })
// Get user input from query string (hx-vars)
let userInput = Astro.url.searchParams.get('user_input') || 'Hello, AI!';
// Prepare the chat messages
let messages = [
{ role: "system", content: "Your system message here." },
{ role: "assistant", content: "Hey there! I'm ready to chat." },
{ role: "user", content: userInput }
];
// Create chat completion with OpenAI
const gptChatResponse = await openai.chat.completions.create({
messages: messages,
model: "gpt-3.5-turbo",
max_tokens: 125,
})
// Extract response
let gptResponse = gptChatResponse.choices[0].message.content
---
<ChatMessage sender='gpt' text={gptResponse}/>
It creates a chat completion using OpenAI's API, and then includes the generated response directly inside an Astro component's props.
Note: The code in the front-matter runs within the server each time you make a request, prior to rendering the component. The response itself only includes the HTML of the rendered component.
This page can now be rendered using HTMX, like so:
<button hx-get="/chat/gpt_response"
hx-vars="{'user_input':'HTMX with Astro is pretty neat.'}"
hx-target="#chat-container"
hx-swap="beforeend">
Click Me
</button>
By the way, they recently added official support for "partial" pages. However, I don't know what difference it makes to set the export const partial = true
inside your .astro file yet.