타입이 지정된 상태, 사는 곳은 URL

useUrlState 는 자신을 쿼리 문자열에 기록하는 React 상태입니다. 객체, 배열, 날짜는 타입을 유지하고, 모든 상태는 공유 가능한 링크가 되며, 새로고침 후에도 유지됩니다. 프로바이더도 보일러플레이트도 필요 없습니다.

  • ~2 KB gzip 압축
  • 의존성 없음
  • TypeScript 우선
  • Next.js / react-router / Remix
  • MIT
npm i state-in-url

useUrlState — 라이브로 시연: next.js

아래에 입력하세요 — URL이 빛나는 것을 보세요

첫 번째 클라이언트 컴포넌트
다른 클라이언트 컴포넌트

URL에서 읽습니다 — props도 context도 없이, 타입과 구조가 유지됩니다

{
name: string
age: undefinedundefined
agree_to_terms: falseboolean
tags: []
}

같은 API, 세 가지 라우터

빠른 시작

1. state 정의하기
state
export const form: Form = {
  name: '',
  age: undefined,
  agree_to_terms: false,
  tags: [],
};

// use `Type` not `Interface`!
type Form = {
  name: string;
  age?: number;
  agree_to_terms: boolean;
  tags: { id: string; value: { text: string; time: Date } }[];
};
2. 어떤 컴포넌트에서든 사용하기
ComponentA
'use client';

import { useUrlState } from 'state-in-url/next';
import { form } from './form';

export const ComponentA = () => {
  // see docs for all possible params https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState
  // useHistory: false to update sp on server component
  const { urlState, setState, setUrl } = useUrlState(form, { useHistory: true }); 

  return <>
    <input
      id="name"
      value={urlState.name} 
      onChange={(ev) => setUrl({ name: ev.target.value })}
      />
    // OR can update state immediately but sync change to url as needed
    <input
      value={urlState.name}
      onChange={(ev) => { setState(curr => ({ ...curr, name: ev.target.value })) }}
      onBlur={() => setUrl()}
    />
    <button onClick={() => setUrl((curr, initial) => initial)}>
      Reset
    </button>
    </>
};
ComponentB
'use client';
import { useUrlState } from 'state-in-url/next';
import { form } from './form';

// "searchParams" used to pass params from Server Components
export const ComponentB = ({ searchParams }: { searchParams?: object }) => {
  const { urlState } = useUrlState(form, { searchParams });

// will be defaultValue from `form` if not in url, no need to check

  return <div>name: {urlState.name}</div>
};
3. state 일부를 다루는 재사용 가능한 훅 만들기
useFormState - custom hook
'use client';

import React from 'react';
import { useUrlState } from 'state-in-url/next';

const form: Form={
  name: '',
  age: undefined,
  agree_to_terms: false,
  tags: [],
};

type Form = {
  name: string;
  age?: number;
  agree_to_terms: boolean;
  tags: {id: string; value: {text: string; time: Date } }[];
};

export const useFormState = ({ searchParams }: { searchParams?: object }) => {
  const { urlState, setUrl: setUrlBase, reset } = useUrlState(form, {
    searchParams,
  });

  // first navigation will push new history entry
  // all following will just replace that entry
  // this way will have history with only 2 entries - ['/url', '/url?key=param']

  const replace = React.useRef(false);
  const setUrl = React.useCallback((
      state: Parameters<typeof setUrlBase>[0],
      opts?: Parameters<typeof setUrlBase>[1]
    ) => {
      setUrlBase(state, { replace: replace.current, ...opts });
      replace.current = true;
  }, [setUrlBase]);

  return { urlState, setUrl, resetUrl: reset };
};

AI 코딩 에이전트를 사용하시나요?

에이전트는 여기서 매번 같은 두 가지를 틀립니다. 상태 형태를 interface 로 입력하는데, 이는 JSONCompatible 제약이 즉시 거부합니다. 그리고 컴포넌트 안에서 기본 상태 객체를 만들어 공유를 조용히 깨뜨립니다. 객체 동일성으로 키가 지정되므로 오류가 발생하지 않고, 두 컴포넌트가 서로를 보지 못하게 될 뿐입니다.

그래서 이 패키지는 6개의 SKILL.md 파일을 제공합니다. 에이전트는 TanStack Intent 를 통해 필요할 때 하나를 로드합니다. 이 파일들은 이 페이지가 아니라 라이브러리와 함께 버전이 관리됩니다.

npx @tanstack/intent@latest install

이미 state-in-url 이(가) 설치된 프로젝트에서 한 번 실행하세요. 그러면 에이전트가 node_modules/state-in-url/skills/ 에서 스킬을 찾습니다.

  • feature-state-hook상태 정의와 useUrlState를 기능 범위 훅으로 감싸기
  • input-handling텍스트 입력, 슬라이더 등 빠르게 변하는 모든 것
  • nextjs-ssrApp Router: searchParams 전달, 레이아웃용 Proxy
  • react-router-remix-setupReact Router v6/v7 또는 Remix v2 설정
  • form-library-integrationreact-hook-form(또는 formik)과 함께 사용
  • shared-state-no-urluseSharedState — URL을 건드리지 않고 공유

소스는 GitHub에 있습니다. Intent 스킬을 로드할 수 없는 에이전트는 대신 llms.txt 을 읽어야 합니다 — 같은 규칙이 한 파일에 압축되어 있습니다.

왜 state-in-url인가?

URL 상태 라이브러리는 존재하지만, 대부분은 설정이 번거롭거나 저장할 수 있는 것에 제한이 있습니다. state-in-url 은(는) 그냥 동작하는 것을 목표로 합니다. React.useState 을(를) 그대로 반영한 API로, URL을 저장소로 사용합니다.

보일러플레이트 없이 상태를 저장하고, 딥 링크를 만들고, 서로 관련 없는 클라이언트 컴포넌트 간에 데이터를 공유하세요. 프로바이더가 필요 없습니다. 구조와 타입은 처음부터 끝까지 유지됩니다. Date 이 들어가면 Date 이 나옵니다.

테스트 우선으로 구축되었으며, 유닛 테스트와 크로스 브라우저 e2e 스위트가 모든 커밋에서 실행됩니다.

Next.js: Suspense 경계 불필요

이 훅은 useSearchParams 을(를) 호출하지 않으므로, 이를 사용하는 컴포넌트는 Suspense 로 감쌀 필요가 없고, 페이지가 사전 렌더링에서 제외되지도 않습니다. PPR과 cacheComponents 도 포함됩니다. URL을 직접 읽고 이후의 모든 변경을 따라갑니다. 예를 들어 history.pushState 을(를), 그 존재조차 모르는 코드에서 호출한 경우도 따라갑니다.

Next.js나 react-router를 사용하지 않으시나요?

encodeState / decodeState 헬퍼는 어떤 프레임워크나 순수 JS에서도 동작합니다. 훅은 그 위의 편의 레이어입니다.

다음 GitHub 페이지 를 확인해 보세요. 스타 하나가 큰 힘이 됩니다.

다른 개발자와 공유하기

Uneed Embed Badge