npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

use-react-utilities

v1.0.6-2

Published

A react utilities for need develop your project similar with vue 3

Readme

React Composable Utilities

A collection of custom React Hooks and utility Components designed to accelerate and simplify your React application development, inspired by the compositional ease of Vue 3.

Key Features

  • Custom Hooks: A set of reusable hooks for common stateful logic.
  • Utility Components: Drop-in components for conditional rendering, loops, and more.
  • TypeScript Ready: Fully written in TypeScript for strong type safety.

Installation

Use npm or your favorite package manager to install.

npm install use-react-utilities

Usage

Here is how to import and use the hooks and components from this library.


useColorName

Fetches the name of a color from its hex value. This is useful for color tools, design systems, or any feature that needs to display a human-readable color name.

import { colorFromName, ref } from 'use-react-utilities';

export default function ColorPicker() {
  const color = ref('#FFF')
  const colorName = colorFromName(color.value);

  return (
    <div>
      <input
        type="color"
        value={color.value}
        onChange={(e) => color.value = e.target.value}
      />
      <p>Color Name: {colorName || 'Loading...'}</p>
    </div>
  );
}

useComputed

Creates a memoized value that only recalculates when its dependencies change. It's perfect for optimizing expensive calculations.

import { useComputed, ref } from 'use-react-utilities';

export default function Counter() {
  const count = ref(0)

  // The `double` value is automatically updated when `count` changes
  const double = useComputed(() => count.value * 2);

  return (
    <div>
      <p>Count: {count.value}</p>
      <p>Double: {double.value}</p>
      <button onClick={() => count.value += 1}>Increment</button>
    </div>
  );
}

useDuplicate

Creates and renders a specified number of component instances. This is useful for storyboarding, testing, or decorative UI elements.

import React from 'react';
import { deleteDuplicate } from 'use-react-utilities';

const users: User[] = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }, // duplicate
  { id: 3, name: "Charlie" },
  { id: 2, name: "Bob" } // duplicate
]

const uniqueUsers = deleteDuplicate(users)
console.log(uniqueUsers)

useFormData

Manages form state efficiently. It simplifies handling input changes and form submissions.

import React from 'react';
import { useFormData, ref } from 'use-react-utilities';

function LoginForm() {
  const data = ref({
    email: '',
    password: ''
  })

  const submitLogin = () => {
    alert(`Logging in with: ${JSON.stringify(data)}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="email" 
        name="email"
        value={data.value.email}
        onChange={(e) => data.value.email = e.target.value}
        placeholder="Email"
      />
      <input 
        type="password" 
        name="password"
        value={data.value.password}
        onChange={(e) => data.value.password = e.target.value}
        placeholder="Password"
      />
      <button type="submit">Login</button>
    </form>
  );
}

useLifeCycle

Provides onMounted and onUnmounted lifecycle hooks, similar to Vue's onMounted and onUnmounted.

import React from 'react';
import { onMounted, onUnmounted, onUpdated, onBeforeUpdate, onBeforeUnmount, onBeforeMount } from 'use-react-utilities';

function MyComponent() {
  onMounted() {
    console.log('Component has been mounted!');
  }
  onUnmounted(){
    console.log('Component will be unmounted!');
  }
  onUpdated(){
    console.log('Component will be updated')
  }
  onBeforeUpdate(){
    console.log('Component will be before updated')
  }
  onBeforeUnmount(){
    alert('Component will be before unmound')
  }
  onBeforeMount(){
    console.log('component wil be before mound')
  }

  return <p>My Component is here.</p>;
}

ref

A custom ref hook that may include additional functionality for handling refs in a more declarative way.

import React from 'react';
import { ref } from 'use-react-utilities';

function FocusInput() {
  const search = ref(0);

  return (
    <div>
      <input onChange={(e) => search.value = e.target.value} type="text" placeholder="Focus me"} />
    </div>
  );
}

useWatch

Runs an effect immediately and automatically re-runs it whenever any of its dependencies change. Unlike useEffect, you don't need to specify a dependency array.

import { useWatch, ref } from 'use-react-utilities';

function Watcher() {
   const search = ref(0);

  useWatch(search.value, (newValue, oldValue) => {
    console.log(`The current count is: ${newValue, oldValue}`);
  });

  return (
    <div>
      <p>Check the console.</p>
      <button onClick={() => search.value += 1}>Increment: {count}</button>
    </div>
  );
}

<If> Component

Use the <If> component for clean, declarative conditional rendering instead of ternary operators.

import React from 'react';
import { ConditionalGroup, If, ElseIf, Else } from 'use-react-utilities/components';

const UserGreeting = ({ user }) => {
  return (
  <ConditionalGroup>
    <If condition={count % 2 === 0}>Hello</If>
    <ElseIf condition={count % 2 === 1}>World</ElseIf>
    <Else>No Something</Else>
  </ConditionalGroup>
  );
};

<Each> Component

Use the <Each> component to iterate over an array without using .map() inside your tsx, making your component cleaner.

import React from 'react';
import { Each } from 'use-react-utilities/components';

const PostList = ({ posts }) => {
  return (
    <ul>
      <Each
        of={posts}
        render={(post, index) => (
          <li key={post.id}>
            {index + 1}. {post.title}
          </li>
        )}
      />
    </ul>
  );
};

<Slot> and <Component>

Use <Component> to create dynamic, polymorphic components and <Slot> to create flexible component layouts that can be filled by parent components.

import type { ReactNode } from 'react'
import { Template, useSlots } from 'use-react-utilities'

interface Product {
  id: number
  name: string
  price: number
  stock: number
}

interface ProductCardProps {
  product: Product
  children: ReactNode
}

interface SlotProps {
  product: Product;
  AddToCart: () => void

}
function ProductCard({ product, children }: Readonly<ProductCardProps>) {
  const { slots, scopedSlots } = useSlots<Product>(children)

  function AddToCart() {
    alert('test add to cart')
  }

  const slotProps = {
    AddToCart
  } as SlotProps;

  const combinedProps: Product & SlotProps = {
    ...product,
    ...slotProps,
  };

  return (
    <div className="border rounded-lg shadow-md overflow-hidden bg-white">
      {slots.header && (
        <div className="bg-gradient-to-r from-blue-500 to-blue-600 text-white p-4">
          {slots.header}
        </div>
      )}

      <div className="p-4">
        {scopedSlots.body ? scopedSlots.body(product) : (
          <div>
            <h3 className="font-bold text-lg">{product.name}</h3>
            <p className="text-gray-600">Rp {product.price.toLocaleString('id-ID')}</p>
          </div>
        )}
      </div>

      <div className="bg-gray-50 p-4 border-t">
        {scopedSlots.footer ? scopedSlots.footer(combinedProps) : slots.footer}
      </div>
    </div>
  )
}

export default function App() {
  const product: Product = {
    id: 1,
    name: "Laptop Gaming",
    price: 15000000,
    stock: 5
  }

  return (
    <div className="min-h-screen bg-gray-100 p-8">
      <div className="max-w-2xl mx-auto space-y-8">
        <ProductCard product={product}>
          <Template name="header">
            🔥 New Product
          </Template>
          <Template name="body">
            {(prod: Product) => (
              <div className="space-y-3">
                <h3 className="text-2xl font-bold text-gray-800">{prod.name}</h3>
                <div className="flex items-center gap-4">
                  <span className="text-3xl font-bold text-blue-600">
                    Rp {prod.price.toLocaleString('id-ID')}
                  </span>
                  <span className={`px-3 py-1 rounded-full text-sm ${prod.stock > 0 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
                    }`}>
                    {prod.stock > 0 ? `Stok: ${prod.stock}` : 'Habis'}
                  </span>
                </div>
                <p className="text-gray-600">
                  Computer gaming
                </p>
              </div>
            )}
          </Template>
          <Template name="footer">
            {
              (props: SlotProps) => (
                <button
                  className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded"
                  onClick={props.AddToCart}
                >
                  Add to Cart
                </button>
              )
            }
          </Template>
        </ProductCard>
      </div>
    </div>
  )
}

IsComponents

Creates dynamic components

import { useState, useEffect } from "react"; 
import { useComputed, ref, onMounted, onUnmounted } from 'use-react-utilities';

const Counter: React.FC<{ name: string }> = ({ name }) => {
  const [count, setCount] = useState<number>(0);

  onMounted(() => {
    console.log(`${name} mounted`);
  })

  onUnmounted(() => {
    console.log(`${name} unmounted`);
  })

  return (
    <div className="p-6 bg-blue-50 rounded-lg">
      <h2 className="text-xl font-bold mb-4">{name}</h2>
      <p className="text-3xl font-bold mb-4">{count}</p>
      <button
        onClick={() => setCount(count + 1)}
        className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
      >
        Increment
      </button>
    </div>
  );
};

export default function Counter() {
  return (
    <IsComponent is={Counter} name="This Name Props Name" />

    <IsComponent is="a" href="https://google.com" target="_blank">
       Open Google
    </IsComponent>

    <IsComponent is="h1" className="text-2xl font-bold">
      Title
    </IsComponent>

    <IsComponent is="div" style={{ padding: '20px', background: '#f0f0f0' }}>
      Content div
    </IsComponent>
  );
}

KeepAlive

Creating active components and not removing values

import { KeepAlive, onMounted,onUnmounted, ref, onActivated } from 'use-react-utilities'

// Demo Components
const Counter: React.FC<{ name: string }> = ({ name }) => {
  const count = ref(0)

  onActivated(() => {
    console.log('page actived' + name)
  })

  onMounted(() => {
      console.log(`${name} mounted`);
  })

  onUnmounted(() => {
    console.log(`${name} unmounted`);
  })

  return (
    <div className="p-6 bg-blue-50 rounded-lg">
      <h2 className="text-xl font-bold mb-4">{name}</h2>
      <p className="text-3xl font-bold mb-4">{count}</p>
      <button
        onClick={() => count.value += 1}
        className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
      >
        Increment
      </button>
    </div>
  );
};

const InputField: React.FC<{ name: string }> = ({ name }) => {
  const text = ref('')

  onActivated(() => {
    console.log('page actived' + name)
  })

  onMounted(() => {
      console.log(`${name} mounted`);
  })

  onUnmounted(() => {
    console.log(`${name} unmounted`);
  })


  return (
    <div className="p-6 bg-green-50 rounded-lg">
      <h2 className="text-xl font-bold mb-4">{name}</h2>
      <input
        type="text"
        value={text.value}
        onChange={(e) => text.value = e.target.value}
        placeholder="Type something..."
        className="w-full px-4 py-2 border rounded"
      />
      <p className="mt-2 text-gray-600">Value: {text.value}</p>
    </div>
  );
};

interface Todo {
  id: number;
  text: string;
}

const Todo: React.FC<{ name: string }> = ({ name }) => {
  const todos = ref<Todo[]>([])
  const input = ref<string>('')

  onActivated(() => {
    console.log('page actived' + name)
  })

  onMounted(() => {
      console.log(`${name} mounted`);
  })

  onUnmounted(() => {
    console.log(`${name} unmounted`);
  })

  const addTodo = () => {
    if (input.trim()) {
      todos.value = [...todos.value, { id: Date.now(), text: input }]
      input.value = ''
    }
  };

  return (
    <div className="p-6 bg-purple-50 rounded-lg">
      <h2 className="text-xl font-bold mb-4">{name}</h2>
      <div className="flex gap-2 mb-4">
        <input
          type="text"
          value={input}
          onChange={(e) => input.value = e.target.value}
          onKeyPress={(e) => e.key === 'Enter' && addTodo()}
          placeholder="Add todo..."
          className="flex-1 px-4 py-2 border rounded"
        />
        <button
          onClick={addTodo}
          className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600"
        >
          Add
        </button>
      </div>
      <div className="space-y-2">
        {todos.value.map((todo) => (
          <div key={todo.id} className="p-2 bg-white rounded">
            {todo.text}
          </div>
        ))}
      </div>
    </div>
  );
};

// Demo App
export default function App() {
  const currentView = ref('counter')

  const getComponent = () => {
    switch (currentView.value) {
      case 'counter':
        return <Counter name="Counter Component" />;
      case 'input':
        return <InputField name="Input Component" />;
      case 'todo':
        return <Todo name="Todo Component" />;
      default:
        return null;
    }
  };

  return (
    <div className="min-h-screen bg-gray-100 p-8">
      <div className="max-w-2xl mx-auto">
        <h1 className="text-3xl font-bold mb-8 text-center">
          React KeepAlive Demo
        </h1>

        <div className="flex gap-4 mb-8 justify-center">
          <button
            onClick={() => currentView.value = 'counter'}
            className={`px-6 py-3 rounded-lg font-semibold transition ${currentView === 'counter'
              ? 'bg-blue-500 text-white'
              : 'bg-white text-gray-700 hover:bg-gray-50'
              }`}
          >
            Counter
          </button>
          <button
            onClick={() => currentView.value = 'input'}
            className={`px-6 py-3 rounded-lg font-semibold transition ${currentView === 'input'
              ? 'bg-green-500 text-white'
              : 'bg-white text-gray-700 hover:bg-gray-50'
              }`}
          >
            Input
          </button>
          <button
            onClick={() => currentView.value = 'todo'}
            className={`px-6 py-3 rounded-lg font-semibold transition ${currentView === 'todo'
              ? 'bg-purple-500 text-white'
              : 'bg-white text-gray-700 hover:bg-gray-50'
              }`}
          >
            Todo
          </button>
        </div>

        <div className="bg-white rounded-xl shadow-lg p-8">
          <KeepAlive name={currentView} max={5}>
            {getComponent()}
          </KeepAlive>
        </div>
      </div>
    </div>
  );
}

Update Version 1.0.5


Teleport

Creates Teleport component to target

import { Teleport, ref } from 'use-react-utilities';

// Demo App
export default function App() {
  const showModal = ref(false);
  const showNotification = ref(false);
  const showInDiv = ref(false);

  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 p-8">
      <div className="max-w-4xl mx-auto">
        <div className="bg-white rounded-lg shadow-lg p-8">
          <h1 className="text-3xl font-bold text-gray-800 mb-2">
            React Teleport Demo
          </h1>

          <div className="space-y-4">
            <button
              onClick={() => (showModal.value = true)}
              className="px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition"
            >
              Open Modal (to body)
            </button>

            <button
              onClick={() => (showNotification.value = true)}
              className="ml-4 px-6 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition"
            >
              Notification (to body)
            </button>

            <button
              onClick={() => (showInDiv.value = true)}
              className="ml-4 px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition"
            >
              Teleport to #target-div
            </button>
          </div>

          {/* Target div untuk demo */}
          <div className="mt-8 p-6 bg-yellow-50 border-2 border-yellow-300 rounded-lg min-h-32">
            <h3 className="font-semibold text-yellow-800 mb-2">
              Target Div (id="target-div")
            </h3>
            <p className="text-yellow-700 text-sm mb-2">
              content show in this div
            </p>
            <div
              id="target-div"
              className="bg-white p-4 rounded border border-yellow-200 min-h-16"
            />
          </div>
        </div>
      </div>

      {/* Modal teleport */}
      {showModal.value && (
        <Teleport to="body">
          <div
            className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
            onClick={(e) => {
              if (e.target === e.currentTarget) showModal.value = false;
            }}
          >
            <div className="bg-white rounded-lg p-8 max-w-md w-full mx-4 shadow-2xl">
              <h2 className="text-2xl font-bold text-gray-800 mb-4">
                {showModal.value ? "true" : "false"} Modal via Teleport
              </h2>
              <p className="text-gray-600 mb-6">
                the modal teleport{" "}
                <code className="bg-gray-100 px-2 py-1 rounded">
                  document.body
                </code>{" "}
                using Teleport!
              </p>
              <button
                onClick={() => (showModal.value = false)}
                className="px-6 py-2 bg-red-600 text-white rounded hover:bg-red-700 transition"
              >
                Tutup
              </button>
            </div>
          </div>
        </Teleport>
      )}

      {/* Notification teleport */}
      {showNotification.value && (
        <Teleport to="body">
          <div className="fixed top-4 right-4 z-50 animate-slide-in">
            <div className="bg-green-600 text-white px-6 py-4 rounded-lg shadow-lg flex items-center gap-3">
              <svg
                className="w-6 h-6"
                fill="none"
                stroke="currentColor"
                viewBox="0 0 24 24"
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M5 13l4 4L19 7"
                />
              </svg>
              <span className="font-medium">
                Notification teleport to body!
              </span>
              <button
                onClick={() => (showNotification.value = false)}
                className="ml-4 hover:bg-green-700 rounded p-1 transition"
              >
                <svg
                  className="w-5 h-5"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth={2}
                    d="M6 18L18 6M6 6l12 12"
                  />
                </svg>
              </button>
            </div>
          </div>
        </Teleport>
      )}

      {/* Teleport ke target div */}
      {showInDiv.value && (
        <Teleport to="#target-div">
          <div className="bg-gradient-to-r from-purple-500 to-pink-500 text-white p-4 rounded-lg shadow-lg">
            <div className="flex items-center justify-between">
              <div>
                <h4 className="font-bold mb-1">Success teleport!</h4>
                <p className="text-sm">render to div with id "target-div"</p>
              </div>
              <button
                onClick={() => (showInDiv.value = false)}
                className="ml-4 hover:bg-white hover:bg-opacity-20 rounded p-2 transition"
              >
                <svg
                  className="w-5 h-5"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth={2}
                    d="M6 18L18 6M6 6l12 12"
                  />
                </svg>
              </button>
            </div>
          </div>
        </Teleport>
      )}
    </div>
  );
}

useTemplateRef

import { useTemplateRef } from 'use-react-utilities'

  const formRef = useTemplateRef()

  <Form ref={formRef}
   ...>


   </Form>

Form Validation

Create Form Validation

import { z } from 'zod';
import * as yup from 'yup';
import { ref, Form, FormField, useTemplateRef } from 'use-react-utilities'

const yupSchema = yup.object({
  email: yup
    .string().email("Email tidak valid"),
  password: yup.string().required("Password wajib diisi"),
  name: yup
    .string()
    .trim()
    .min(2, "Nama wajib diisi")
    .required("Nama wajib diisi"),
  file: yup
    .mixed<File>()
    .required("File wajib diisi")
    .test("fileSize", "Ukuran file maksimal 2MB", (value) => {
      return value instanceof File ? value.size <= 2 * 1024 * 1024 : false;
    })
    .test("fileType", "Hanya menerima file gambar (jpg/png)", (value) => {
      return value instanceof File
        ? ["image/jpeg", "image/png"].includes(value.type)
        : false;
    }),
});

// choose validation zod or yup
const zodSchema = z.object({
  email: z.string().email("Email tidak valid"),
  password: z.string().min(8, "Password minimal 8 karakter"),
  name: z.string().min(2, "Nama wajib diisi"),
  file: z.any(),
})


type FormValues = yup.InferType<typeof yupSchema>;
export default function MyForm() {

  const formState = ref<FormValues>({
    email: "",
    password: "",
    name: "",
    file: undefined as unknown as File,
  })

  const handleSubmit = (e: { data: FormValues }) => {
    console.log("Form submitted ✅:", e.data);
  };
  const handleError = (e: { errors: any[] }) => {
    console.log("Validation errors ❌:", e.errors);
  };

  const formRef = useTemplateRef()

  const validateClick = async () => {
    formRef?.validate({ name: ['email'] })
  };

  return (
    <>
      <button onClick={validateClick}>validate path </button>
      <Form
        ref={formRef}
        state={formState.value}
        schema={zodSchema} // ✅ validasi pakai Zod
        onSubmit={handleSubmit}
        onError={handleError}
      >
        <FormField label="Email" name="email">
          {({ onChange }) => (
            <div>
              <input
                type="email"
                value={formState.value.email}
                onChange={(e) => {
                  formState.value.email = e.target.value
                  onChange()
                }}
                className="border p-2 rounded w-full"
              />
              {/* {error && <span className="text-red-500">{error}</span>} */}
            </div>
          )}
        </FormField>

        <FormField label="Password" name="password">
          {({ onInput, error }) => (
            <div>
              <input
                type="password"
                value={formState.value.password}
                onChange={(e) => {
                  formState.value.password = e.target.value

                }}
                onInput={onInput}
                className="border p-2 rounded w-full"
              />
              {/* {error && <span className="text-red-500">{error}</span>} */}
            </div>
          )}
        </FormField>

        <FormField label="Nama" name="name">
          {({ onChange }) => (
            <div>
              <input
                type="text"
                value={formState.value.name}
                onChange={(e) => {
                  formState.value.name = e.target.value
                  onChange()
                }}
                className="border p-2 rounded w-full"
              />
              {/* {error && <span className="text-red-500">{error}</span>} */}
            </div>
          )}
        </FormField>

        <FormField label="File Upload" name="file">
          {({ onChange, error }) => (
            <div>
              <input
                type="file"
                onChange={(e) => {
                  const file = e.target.files?.[0];
                  if (file) {
                    formState.value.file = file;
                  }
                  onChange();
                }}
                className="border p-2 rounded w-full"
              />
              {error && <span className="text-red-500">{error}</span>}
            </div>
          )}
        </FormField>

        <button type="submit" className="mt-4 px-4 py-2 bg-blue-500 text-white rounded">
          Submit
        </button>
      </Form>
    </>
  );
}

useHead

useHead({
    title: 'My Page',
    titleTemplate: (title) => 'My Site | ' + title, // or (title) => `${title} - My Site`
    base: { href: '/' },
    meta: [
      { name: 'description', content: 'Page description' },
      { property: 'og:title', content: 'My Page' },
      { charset: 'utf-8' }
    ],
    link: [
      { rel: 'canonical', href: 'https://example.com' },
      { rel: 'icon', href: '/favicon.ico', type: 'image/x-icon' }
    ],
    style: [
      { innerHTML: 'body { margin: 0; }' }
    ],
    script: [
      { src: 'https://example.com/script.js', async: true }
    ],
    noscript: [
      { innerHTML: '<img src="https://example.com/pixel.gif" />' }
    ],
    htmlAttrs: {
      lang: 'id',
      dir: 'ltr'
    },
    bodyAttrs: {
      class: 'dark-mode'
    }
  });

useSeoMeta

useSeoMeta({
    title: 'Create Title',
    description: 'Description page for SEO',
    keywords: ['react', 'typescript', 'seo'],
    ogTitle: 'title for Social Media',
    ogImage: 'https://example.com/image.jpg',
    twitterCard: 'summary_large_image',
    author: 'John Doe'
});

Update Version 1.0.5-x

  • Update form add custom label formfield
  • fixing import useTemplateRef
  • add required formField
  • add ref onSubmit form

Update Version 1.0.6-*

  • add define page
  • add page meta

example

child components

useDefinePage({
    layout: true,
    permission: ['admin'],
});

parent components

const {layout, permission} = usePage()

if (!layout) return null

return (
    <>
     <div>Layout configuration</div>
    </>
)

Contributing

If you would like to contribute, please feel free to open an issue or a pull request.

License

ISC