@uniweb/kit

中文文档
1.7.2发布于 2026/11/03Apache-2.0ES Module

Standard component library for Uniweb foundations

原始使用文档
文档目录33 节
  1. Installation
  2. Tree-Shaking Benefits
  3. Quick Start
  4. Components
  5. Link
  6. Image
  7. SafeHtml
  8. Icon
  9. SocialIcon
  10. Typography
  11. Media
  12. FileLogo
  13. MediaIcon
  14. Asset
  15. Hooks
  16. useWebsite
  17. useActiveRoute
  18. useScrolled
  19. useMobileMenu
  20. useAccordion
  21. useInView
  22. useGridLayout
  23. Theme Hooks
  24. Search
  25. useSearch
  26. useSearchPrefetch
  27. useSearchShortcut
  28. createSearchClient
  29. Styled Components
  30. Utilities
  31. Architecture
  32. Why bundle kit but externalize core?
  33. License

@uniweb/kit

Standard component library for Uniweb foundations. Tree-shakeable utilities, components, and hooks for building foundation components.

Installation

npm install @uniweb/kit

Tree-Shaking Benefits

Kit is designed to be bundled into your foundation (not externalized like @uniweb/core). This means:

  • Only what you use is bundled — Import 3 components? Only those 3 end up in your foundation
  • No runtime overhead — Unused code is eliminated at build time
  • Customizable — Override or extend any component without carrying dead code
  • Small foundations — A minimal foundation using just Link and useWebsite stays tiny
// vite.config.js - Kit is bundled, core is external
export default {
  build: {
    rollupOptions: {
      external: ['react', 'react-dom', 'react-router-dom', '@uniweb/core']
      // Note: @uniweb/kit is NOT in external — it gets tree-shaken
    }
  }
}

Quick Start

import { Link, Image, useWebsite } from '@uniweb/kit'

function Hero({ content }) {
  const { localize } = useWebsite()

  return (
    <div>
      <Image src={content.images[0]?.url} alt="Hero" />
      <h1>{localize({ en: 'Welcome', es: 'Bienvenido' })}</h1>
      <Link to="/about">Learn More</Link>
    </div>
  )
}

Components

Smart link component with routing and downloads. A link that leaves the site, downloads a file, writes an email or places a call gets a tooltip saying so; an internal link gets none.

import { Link } from '@uniweb/kit'

<Link to="/about">About</Link>
<Link href="https://github.com">GitHub</Link>
<Link href="/files/report.pdf">Download Report</Link>
<Link href="mailto:[email protected]">Contact</Link>
PropTypeDescription
to / hrefstringDestination URL
titlestringTooltip. If omitted, one is generated only where the link leaves the site, downloads, writes an email or places a call
targetstringLink target
downloadbooleanForce download behavior

Image

Versatile image component with filters and size presets.

import { Image } from '@uniweb/kit'

<Image src="/photo.jpg" alt="Photo" />
<Image src="/photo.jpg" filter={{ grayscale: 100 }} />
<Image src="/team/ada.jpg" alt="Ada" size="lg" rounded />
PropTypeDescription
src / urlstringImage URL
altstringAlt text
sizestringPreset: xs, sm, md, lg, xl, 2xl, full
roundedboolean|stringBorder radius
filterobjectCSS filters: blur, brightness, contrast, grayscale, saturate, sepia

SafeHtml

Safely render HTML, resolving page: links and the base path.

import { SafeHtml } from '@uniweb/kit'

<SafeHtml value="<p>Hello <strong>World</strong></p>" />
<SafeHtml value='<a href="page:about">About</a>' />

Icon

SVG icon component with built-in icons and URL loading.

import { Icon } from '@uniweb/kit'

<Icon name="check" size="24" color="green" />
<Icon url="/icons/custom.svg" />
<Icon svg="<svg>...</svg>" />

Built-in: check, alert, user, heart, settings, star, close, menu, chevronDown, chevronRight, externalLink, download, play

SocialIcon

Social media platform icons with automatic detection.

import { SocialIcon, getSocialPlatform, filterSocialLinks } from '@uniweb/kit'

<SocialIcon platform="twitter" size={24} />
<SocialIcon url="https://twitter.com/example" />

// Utilities
getSocialPlatform('https://linkedin.com/in/user')  // 'linkedin'
filterSocialLinks(links)  // Filter to only social links

Supported: facebook, twitter, x, linkedin, instagram, youtube, github, medium, pinterest, tiktok, discord, mastodon, bluesky, email, phone, orcid, researchgate, googlescholar

Typography

Smart typography components for rendering semantic parser output.

import { Text, H1, H2, P, PlainText } from '@uniweb/kit'

<H1 text="Main Title" />
<H2 text={["Multi-line", "Subtitle"]} />
<P text="A paragraph of content" />
<P text={["First paragraph", "Second paragraph"]} />

// Plain text (HTML shown as text)
<PlainText text="Show <strong>tags</strong> as text" />
PropTypeDescription
textstring|string[]Content to render
asstringTag: 'h1'-'h6', 'p', 'div', 'span'
htmlbooleanRender as HTML (default: true)
lineAsstringTag for array items

Aliases: H1, H2, H3, H4, H5, H6, P, Span, Div, PlainText

Media

Video player for YouTube, Vimeo, and local videos.

import { Media } from '@uniweb/kit'

<Media src="https://youtube.com/watch?v=abc123" />
<Media src="/videos/intro.mp4" controls />
<Media src="https://youtube.com/..." thumbnail="/poster.jpg" facade />

File type icons based on filename.

import { FileLogo } from '@uniweb/kit'

<FileLogo filename="report.pdf" size="32" />

MediaIcon

Platform icons (YouTube, Vimeo, etc.).

import { MediaIcon } from '@uniweb/kit'

<MediaIcon type="youtube" size="24" />

Asset

File preview with download functionality.

import { Asset } from '@uniweb/kit'

<Asset value="/files/document.pdf" />

Hooks

useWebsite

Access website instance and utilities.

import { useWebsite } from '@uniweb/kit'

function MyComponent() {
  const {
    website,      // Website instance
    localize,     // Localize multilingual values
    makeHref,     // Resolve page: references to routes
    getLanguage,  // Current language code
    getLanguages  // Available languages
  } = useWebsite()

  return <div>{localize({ en: 'Hello', fr: 'Bonjour' })}</div>
}

useActiveRoute

Detect active navigation state.

import { useActiveRoute } from '@uniweb/kit'

function NavLink({ page }) {
  const { isActive, isActiveOrAncestor } = useActiveRoute()

  return (
    <Link
      to={page.route}
      className={isActiveOrAncestor(page) ? 'font-bold' : ''}
    >
      {page.title}
    </Link>
  )
}

useScrolled

Detect scroll position for sticky headers.

import { useScrolled } from '@uniweb/kit'

function Header() {
  const scrolled = useScrolled(50)  // Threshold in pixels

  return (
    <header className={scrolled ? 'shadow-md' : ''}>
      ...
    </header>
  )
}

useMobileMenu

Mobile menu state management.

import { useMobileMenu } from '@uniweb/kit'

function Navbar() {
  const { isOpen, toggle, close } = useMobileMenu()

  return (
    <>
      <button onClick={toggle}>Menu</button>
      {isOpen && <MobileMenu onClose={close} />}
    </>
  )
}

useAccordion

Accordion/FAQ state management.

import { useAccordion } from '@uniweb/kit'

function FAQ({ items }) {
  const { isOpen, toggle } = useAccordion()

  return items.map((item, i) => (
    <div key={i}>
      <button onClick={() => toggle(i)}>{item.question}</button>
      {isOpen(i) && <p>{item.answer}</p>}
    </div>
  ))
}

useInView

Viewport intersection detection for lazy loading and animations.

import { useInView, useIsInView } from '@uniweb/kit'

function AnimatedSection() {
  const { ref, inView } = useInView({ threshold: 0.2, once: true })

  return (
    <div ref={ref} className={inView ? 'animate-fade-in' : 'opacity-0'}>
      Content appears when scrolled into view
    </div>
  )
}

// Simple boolean version
function LazyImage({ src }) {
  const [ref, isInView] = useIsInView()
  return <div ref={ref}>{isInView && <img src={src} />}</div>
}

useGridLayout

Responsive grid utilities.

import { useGridLayout, getGridClasses } from '@uniweb/kit'

function Gallery({ items }) {
  const { columns } = useGridLayout(items.length, { maxColumns: 4 })

  return (
    <div className={getGridClasses(columns)}>
      {items.map(item => <Card key={item.id} {...item} />)}
    </div>
  )
}

Theme Hooks

Access site theming data at runtime.

import {
  useThemeData,
  useThemeColor,
  useThemeColorVar,
  useColorContext,
  useAppearance
} from '@uniweb/kit'

function ThemedComponent({ block }) {
  // Full theme access
  const theme = useThemeData()
  const palettes = theme?.getPaletteNames()  // ['primary', 'secondary', ...]

  // Get specific color
  const primaryColor = useThemeColor('primary', 500)  // '#3b82f6'
  const primaryVar = useThemeColorVar('primary', 600)  // 'var(--primary-600)'

  // Context-aware (light/medium/dark sections)
  const context = useColorContext(block)  // 'light' | 'medium' | 'dark'

  // Dark mode
  const { scheme, toggle, canToggle } = useAppearance()

  return (
    <div style={{ color: primaryColor }}>
      {canToggle && (
        <button onClick={toggle}>
          {scheme === 'dark' ? 'Light' : 'Dark'}
        </button>
      )}
    </div>
  )
}

Full-text search powered by Fuse.js. Requires fuse.js as a peer dependency in your foundation.

npm install fuse.js

useSearch

Main search hook with debouncing and state management.

import { useSearch, useWebsite } from '@uniweb/kit'

function SearchComponent() {
  const { website } = useWebsite()
  const { query, results, isLoading, isEnabled, preload } = useSearch(website)

  if (!isEnabled) return null

  return (
    <div>
      <input onChange={e => query(e.target.value)} placeholder="Search..." />
      {isLoading && <span>Searching...</span>}
      {results.map(r => (
        <a key={r.id} href={r.href}>{r.title}</a>
      ))}
    </div>
  )
}

useSearchPrefetch

Prefetches the search index on hover, focus or touch instead of on page load.

import { useSearchPrefetch, useSearchShortcut } from '@uniweb/kit'

function SearchButton({ onClick }) {
  const { website } = useWebsite()
  const { triggerPreload, prefetchProps } = useSearchPrefetch(website)

  // Cmd/Ctrl+K shortcut with preload
  useSearchShortcut({
    onOpen: onClick,
    onPreload: triggerPreload,
  })

  return (
    <button onClick={onClick} {...prefetchProps}>
      Search
    </button>
  )
}

A visitor who never searches never downloads the index. It fetches once.

useSearchShortcut

Keyboard shortcut for opening search.

import { useSearchShortcut } from '@uniweb/kit'

// Simple
useSearchShortcut(() => setSearchOpen(true))

// With preload on shortcut
useSearchShortcut({
  onOpen: () => setSearchOpen(true),
  onPreload: () => searchClient.preload()
})

createSearchClient

Low-level search client for advanced use.

import { createSearchClient } from '@uniweb/kit'

const client = createSearchClient(website, {
  fuseOptions: { threshold: 0.3 },
  defaultLimit: 10
})

// Query — returns SearchResult[]
const results = await client.query('authentication', { limit: 5 })

// Same query, plus how many matched before `limit` — the 47 in "showing 10 of 47".
// `total` is null when the active provider cannot say (a deployment fact, not an
// error): the local index always knows it; an endpoint knows it only if it
// reports one. Render the count conditionally, the results unconditionally.
const { results: page, total } = await client.queryWithTotal('authentication', { limit: 5 })

// Preload index
await client.preload()

// Check status
client.isEnabled()
client.getIndexUrl()

Styled Components

Pre-styled components with Tailwind CSS.

import { Section, SidebarLayout, Disclaimer } from '@uniweb/kit'

<Section width="lg" padding="md" className="bg-gray-50">
  <h1>Welcome</h1>
</Section>

<SidebarLayout sidebar={<Nav />} sidebarPosition="left">
  <main>Content</main>
</SidebarLayout>

<Disclaimer
  title="Terms of Service"
  content="<p>Please read our terms...</p>"
  triggerText="View Terms"
/>

Utilities

import { cn, stripTags, isExternalUrl, isFileUrl, detectMediaType } from '@uniweb/kit'

// Merge Tailwind classes (uses tailwind-merge)
cn('px-4 py-2', 'bg-blue-500', condition && 'opacity-50')

// Strip HTML tags
stripTags('<p>Hello</p>')  // "Hello"

// URL utilities
isExternalUrl('https://google.com')  // true
isFileUrl('/files/doc.pdf')          // true
detectMediaType('https://youtube.com/...')  // 'youtube'

Architecture

┌─────────────────────────────────────────────────────────────┐
│  Foundation (your code)                                     │
│    ├── imports @uniweb/kit (bundled, tree-shaken)           │
│    └── @uniweb/core marked as external                      │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  @uniweb/runtime (browser)                                  │
│    ├── Loads foundation dynamically                         │
│    ├── Provides @uniweb/core singleton                      │
│    └── Orchestrates React rendering                         │
└─────────────────────────────────────────────────────────────┘

Why bundle kit but externalize core?

  • Kit: Different foundations may use different subsets of kit. Tree-shaking ensures each foundation only includes what it uses.

  • Core: Contains the Website, Page, and Block classes that must be singletons. The runtime provides these — foundations reference them via the external import.

License

Apache-2.0

版本与发布记录

@uniweb/kit 共发布 242 个版本,首次发布于 2026/01/17;过去 12 个月发布了 242 个版本。

版本发布日期距今
1.7.2latest2026/11/03今天
1.7.12026/11/021 天前
1.7.02026/11/021 天前
1.6.162026/11/021 天前
1.6.152026/11/021 天前
1.6.142026/11/011 天前

关于 @uniweb/kit 的常见问题

@uniweb/kit 是什么?

Uniweb基础的标准组件库

如何安装 @uniweb/kit?

在项目目录运行 npm install @uniweb/kit,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.0.0)、react-dom(^19.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@uniweb/kit 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/uniweb__kit 是否存在。

@uniweb/kit 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@uniweb/kit 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.12。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@uniweb/kit 可以商用吗?

@uniweb/kit 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@uniweb/kit 还在维护吗?

最新版本 1.7.2 发布于 2026/11/03(今天);至今共发布 242 个版本,其中过去 12 个月发布了 242 个;首次发布于 2026/01/17。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。