npm package: @gringow/gringow-react
Description: React bindings for Gringow AI-powered translation tool
Author: Renato Gaspar
Latest version: 0.3.4

# @gringow/gringow-react

[![npm version](https://img.shields.io/npm/v/@gringow/gringow-react)](https://www.npmjs.com/package/@gringow/gringow-react)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

React bindings for Gringow AI-powered translations. Provides declarative template string translations with runtime language switching and smart caching. Built on [@gringow/gringow-shadow](https://www.npmjs.com/package/@gringow/gringow-shadow) Web Components.

## Features

- ⚛️ **React 19+ Support** - Uses modern React features and hooks
- 🏷️ **Template Literal Syntax** - Translate with \`g\`Hello ${name}\`\`
- 🔄 **Runtime Language Switching** - Change languages without reload
- 💾 **Smart Caching** - Automatic cache lookup and fallback
- 🌐 **SSR Compatible** - Server-side rendering support
- 🎯 **Type-Safe** - Full TypeScript definitions included

## Installation

```bash
# Using pnpm
pnpm add @gringow/gringow @gringow/gringow-react

# Using npm
npm install @gringow/gringow @gringow/gringow-react

# Using yarn
yarn add @gringow/gringow @gringow/gringow-react
```

### Requirements

- **React 19+**
- **A Gringow cache file** (generated by [@gringow/gringow-cli](https://www.npmjs.com/package/@gringow/gringow-cli) or [@gringow/gringow-vite](https://www.npmjs.com/package/@gringow/gringow-vite))

## Quick Start

### 1. Initialize Gringow (Client-Side)

```tsx
// app-bootstrap.tsx (client-only)
import { GringowStore } from '@gringow/gringow-react/store'

// Configure store
GringowStore().language = 'en-US'
GringowStore().cacheUrl = '/gringow/gringow.json'

// Fetch cache once
await GringowStore.fetchCache()
```

### 2. Use Translations in Components

```tsx
// Welcome.tsx
// For Vite/CSR apps:
import { g } from '@gringow/gringow-react/vite'
// For Next.js apps (SSR-aware):
// import { g } from '@gringow/gringow-react/nextjs'

export function Welcome({ name }: { name: string }) {
  return (
    <div>
      <h1>{g`Hello ${name}, welcome back!`}</h1>
      <p>{g`You have new messages`}</p>
    </div>
  )
}
```

### 3. Add Language Switcher

```tsx
// LanguageSwitcher.tsx
import { changeLanguage } from '@gringow/gringow-react'

export function LanguageSwitcher() {
  return (
    <div>
      <button onClick={() => changeLanguage('en-US')}>English</button>
      <button onClick={() => changeLanguage('pt-BR')}>Português</button>
      <button onClick={() => changeLanguage('fr-CA')}>Français</button>
    </div>
  )
}
```

## Usage Examples

### Basic Translation

```tsx
import { g } from '@gringow/gringow-react/vite'

function Greeting() {
  return <h1>{g`Welcome to Gringow`}</h1>
}
```

### Dynamic Content

```tsx
import { g } from '@gringow/gringow-react/vite'

function UserProfile({ user }: { user: { name: string; role: string } }) {
  return (
    <div>
      {g`Hello ${user.name}!`}
      {g`Your role: ${user.role}`}
    </div>
  )
}
```

### Full Application Example

```tsx
// main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { GringowStore } from '@gringow/gringow-react/store'
import App from './App'

// Bootstrap Gringow
async function init() {
  GringowStore.cacheUrl = '/gringow/gringow.json'
  GringowStore.language = document.documentElement.lang || 'en-US'
  await GringowStore.fetchCache()

  ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  )
}

init()
```

```tsx
// App.tsx
import { changeLanguage } from '@gringow/gringow-react'
import { g } from '@gringow/gringow-react/vite'

export default function App() {
  const [user] = React.useState({ name: 'Alice', unread: 5 })

  return (
    <div>
      <nav>
        <button onClick={() => changeLanguage('en-US')}>EN</button>
        <button onClick={() => changeLanguage('pt-BR')}>PT</button>
        <button onClick={() => changeLanguage('es-ES')}>ES</button>
      </nav>

      <main>
        <h1>{g`Welcome ${user.name}!`}</h1>
        <p>{g`You have ${user.unread} unread messages`}</p>
        <button>{g`Mark all as read`}</button>
      </main>
    </div>
  )
}
```

### With Vite

```tsx
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import GringowVitePlugin from '@gringow/gringow-vite'

export default defineConfig({
  plugins: [
    react(),
    GringowVitePlugin()
  ]
})
```

## API Reference

### `g` Template Tag

Main translation function using tagged template literals.

```typescript
function g(strings: TemplateStringsArray, ...values: unknown[]): ReactNode
```

**Example:**
```tsx
{g`Hello ${name}!`}
{g`You have ${count} messages`}
```

The `g` helper:
1. Flattens the template string
2. Creates a cache ID via content hashing
3. Returns a `<g-gringow>` custom element
4. Falls back to original string if cache unavailable

### GringowStore

Singleton store for managing translation cache and language state.

#### Properties

```typescript
// Active language (defaults to document.documentElement.lang)
GringowStore.language: string | null

// Cache file URL
GringowStore.cacheUrl: string | null

// Loaded cache object
GringowStore.cache: GringowCache | null
```

#### Methods

```typescript
// Fetch and cache translations
await GringowStore.fetchCache(): Promise<void>

// Get translation for specific cache ID
await GringowStore.getCacheItem(cacheId: string): Promise<string | null>

// Manually set cache
GringowStore.setCache(cache: GringowCache): void
```

#### Configuration Fallbacks

`cacheUrl` resolution order:
1. `GringowStore.cacheUrl` (programmatic)
2. `<html data-gringow-cache-url="/path">`
3. `<meta name="gringow-cache-url" content="/path">`

### changeLanguage()

Convenience function for language switching.

```typescript
function changeLanguage(lang: string): void
```

**Example:**
```tsx
<button onClick={() => changeLanguage('pt-BR')}>Português</button>
```

Dispatches a global `LanguageChangeEvent` that updates all mounted components.

### LanguageChangeEvent

Custom event for language changes.

```typescript
class LanguageChangeEvent extends CustomEvent<{ lang: string }>

// Constants
LanguageChangeEvent.EVENT_NAME: 'language-change'

// Factory method
LanguageChangeEvent.create(lang: string): LanguageChangeEvent
```

**Manual usage:**
```tsx
window.dispatchEvent(LanguageChangeEvent.create('fr-CA'))

// Listen for changes
window.addEventListener(LanguageChangeEvent.EVENT_NAME, (event) => {
  console.log('Language changed to:', event.detail.lang)
})
```

## Development scripts
```bash
pnpm run build   # Compile TypeScript to dist/
pnpm run watch   # Rebuild on changes during local development
```

## License
MIT © Renato Gaspar

## How It Works

1. **Template Literal** - `g` tag flattens strings and generates cache IDs
2. **Custom Element** - Returns `<g-gringow>` Web Component (from shadow package)
3. **Cache Lookup** - Component queries `GringowStore` for translations
4. **Fallback** - Shows original text if cache unavailable or SSR
5. **Reactivity** - Updates automatically on `LanguageChangeEvent`

## SSR Support

The package handles server-side rendering gracefully:

```tsx
// Server renders original text
{g`Hello ${name}`} // Outputs: "Hello Alice"

// Client hydrates and replaces with translation
{g`Hello ${name}`} // Outputs: "Olá Alice" (if pt-BR)
```

The `<g-gringow>` element only translates on the client after cache loads.

## Module Exports

```typescript
// Main export — store, language utilities, and component helpers
import { gid, Gringow, changeLanguage, LanguageChangeEvent } from '@gringow/gringow-react'

// g helper for Vite / client-side rendering (CSR)
import { g } from '@gringow/gringow-react/vite'

// g helper for Next.js — SSR-aware (server + client detection)
import { g } from '@gringow/gringow-react/nextjs'

// Store export
import { GringowStore } from '@gringow/gringow-react/store'
```

> **Note**: `g` is intentionally split into platform-specific subpaths.
> Use `/vite` for Vite/CSR apps and `/nextjs` for Next.js (SSR) apps.
> The main package no longer re-exports `g` to avoid bundling the wrong runtime.

## Development

```bash
# Install dependencies
pnpm install

# Build the package
pnpm run build

# Watch mode
pnpm run watch
```

## Best Practices

1. **Initialize store once** - Configure `cacheUrl`/`language` early in your app entry point
2. **Fetch cache early** - Call `GringowStore.fetchCache()` before rendering
3. **Use Vite plugin** - Automatically extracts translations during build
4. **Keep fallbacks** - Original strings serve as loading state
5. **Cache versioning** - Bust cache when translations update

## Related Packages

Gringow provides a complete ecosystem for AI-powered translation:

- **[@gringow/gringow](https://www.npmjs.com/package/@gringow/gringow)** - Core translation library
- **[@gringow/gringow-vite](https://www.npmjs.com/package/@gringow/gringow-vite)** - Vite plugin for build-time extraction
- **[@gringow/gringow-react](https://www.npmjs.com/package/@gringow/gringow-react)** - React hooks and components
- **[@gringow/gringow-shadow](https://www.npmjs.com/package/@gringow/gringow-shadow)** - Web Component layer
- **[@gringow/gringow-cli](https://www.npmjs.com/package/@gringow/gringow-cli)** - CLI tool
- **[@gringow/gringow-nextjs](https://www.npmjs.com/package/@gringow/gringow-nextjs)** - Next.js plugin (experimental)

## Resources

- [Documentation](https://gringow.dev)
- [GitHub Repository](https://github.com/rntgspr/gringow)
- [Example: React + Vite](https://github.com/rntgspr/gringow/tree/main/example-gringow-vite)

## License

MIT © [Renato Gaspar](https://github.com/rntgspr)

---

**Need Help?** Open an issue on [GitHub](https://github.com/rntgspr/gringow/issues)