Skip to content

Conversation

@nakasyou
Copy link
Contributor

@nakasyou nakasyou commented May 4, 2025

This pull request introduces a new middleware, htmlHead, for injecting HTML <head> elements dynamically or statically into responses. The changes include adding the middleware implementation, its corresponding tests, and updating relevant configuration files to integrate the new functionality.

I expect this middleware is used with dynamic CSS generation such as UnoCSS and Tailwind CSS, without pre building:

import { Hono } from 'hono'
import { htmlHead } from 'hono/html-head'
import { createGenerator } from '@unocss/core'
import presetUno from '@unocss/preset-uno'

const app = new Hono()

const generator = createGenerator({ presets: [presetUno()] })
app.use(htmlHead(async (c, html) => {
  const style = await uno.generate(html, { preflights: false })
  return `<style>${style}</style>`
}))

app.get('/', c => {
  return c.render(<div class="text-2xl">Hello World</div>)
})

Of course, you can use renderer, but you can't use renderer two or more at same time, so it's difficult to create middleware which injects custom head elements into HTML without this middleware.

The author should do the following, if applicable

  • Add tests
  • Run tests
  • bun run format:fix && bun run lint:fix to format the code
  • Add TSDoc/JSDoc to document the code

@codecov
Copy link

codecov bot commented May 4, 2025

Codecov Report

Attention: Patch coverage is 49.29577% with 36 lines in your changes missing coverage. Please review.

Project coverage is 91.07%. Comparing base (ebc7e4b) to head (aa32ccc).
Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
src/middleware/html-head/index.ts 49.29% 36 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4125      +/-   ##
==========================================
- Coverage   91.34%   91.07%   -0.28%     
==========================================
  Files         168      169       +1     
  Lines       10700    10773      +73     
  Branches     3148     3085      -63     
==========================================
+ Hits         9774     9811      +37     
- Misses        925      961      +36     
  Partials        1        1              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@yusukebe
Copy link
Member

yusukebe commented May 4, 2025

Hi @nakasyou !

Thank you for the PR. I have some thoughts about injecting HTML head elements. I'll comment later.

nakasyou and others added 2 commits May 10, 2025 11:45
Co-authored-by: EdamAmex <121654029+EdamAme-x@users.noreply.github.com>
Co-authored-by: EdamAmex <121654029+EdamAme-x@users.noreply.github.com>
@yusukebe
Copy link
Member

Hey @nakasyou

How about using the feature of React 19 that hoists style tags in head? This is also implemented in hono/jsx.

import { createGenerator } from '@unocss/core'
import presetUno from '@unocss/preset-uno'
import { Hono } from 'hono'
import { jsxRenderer } from 'hono/jsx-renderer'

const app = new Hono()

app.use(
  jsxRenderer(async ({ children }) => {
    const HTML = (
      <html>
        <head />
        <body>
          <div>{children}</div>
        </body>
      </html>
    )
    const uno = await createGenerator({
      presets: [presetUno()],
    })
    const { css } = await uno.generate(HTML.toString())
    return (
      <>
        {HTML}
        <style>{css}</style>
      </>
    )
  })
)

app.get('/', async (c) => {
  return c.render(<div className='text-2xl'>Hello World</div>)
})

export default app

but you can't use renderer two or more at same time, so it's difficult to create middleware which injects custom head elements into HTML without this middleware.

I don't know your specific use cases, but I think you can resolve the issue by injecting props into head elements with that React feature. What do you think of it?

@nakasyou
Copy link
Contributor Author

nakasyou commented May 19, 2025

Hi @yusukebe, sorry for late reply.

I don't know your specific use cases, but I think you can resolve the issue by injecting props into head elements with that React feature. What do you think of it?

Think of creating a middleware that injects props into head, please.

import { unoCSS } from 'my-unocss-middleware-for-hono' // internals using other jsxRenderer

app.use(unoCSS())
app.use(
  jsxRenderer(async ({ children }) => {
    return  <html>
        <head />
        <body>
          <header>Menu</header>
          <div>{children}</div>
        </body>
      </html>
    )
  })
)

Currently, we can't use renderer two or more at same time. What do you think about it?

@yusukebe
Copy link
Member

Hi @nakasyou

In my opinion, we need to consider this feature not only the head tag.

I know injecting content into HTML on the response is a common use case. There are some practices that use HTMLRewriter on Cloudflare Workers to inject the content into HTML tags. That API is also implemented in Bun, and you can find WASM implementations that include supporting Deno:

I believe that using HTMLRewriter will resolve the issue.

You may want to add the feature specifically for the head tag. But the HTMLRewriter approach is reasonable. It can handle HTML tags with jQuery-like selectors, not regexes. It will support other use cases not involving the head tag, for example, changing the links of strings in href or src for moving the content https://oldhost/abc.jpg to https://newhost/abc.jpg, or adding ad scripts at the bottom of HTML.

@nakasyou
Copy link
Contributor Author

Hi @yusukebe, thank you for telling your opinion and I see that this shouldn't be included in core. Closing this.

@nakasyou nakasyou closed this May 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants