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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@medway-ui/native

v1.14.0

Published

Medway UI components for React Native (iOS/Android via Expo)

Readme

@medway-ui/native

Componentes React Native para iOS e Android (Expo). Mesma API visual do @medway-ui/core, construída sobre NativeWind + @rn-primitives.

Versão mínima recomendada para setup brownfield via npm: 1.1.2+


Instalação

npm install @medway-ui/native @medway-ui/core @medway-ui/icons \
  class-variance-authority clsx tailwind-merge nativewind@^4 tailwindcss@^3.4 \
  react-native-reanimated react-native-svg

# @medway-ui/native/setup importa o react-navigation (via createMedwayUiNavigator)
npx expo install @react-navigation/native @react-navigation/native-stack

# Expo (alinha as versões com o seu SDK)
# expo-linear-gradient + masked-view: peers opcionais para o anel `animated` do IndexLevel
# e as variantes com gradiente do Toast (fallback para cor sólida quando ausentes)
npx expo install react-native-reanimated react-native-svg expo-linear-gradient @react-native-masked-view/masked-view

react-native-safe-area-context é uma peer dependency obrigatória: o ToastProvider importa o pacote para ler o inset superior (o React Navigation também exige ele). Apps Expo normalmente já têm (e um SafeAreaProvider na raiz via React Navigation). Sem um SafeAreaProvider, o toast volta para o offset anterior de pt-12.

npx expo install react-native-safe-area-context

A sombra do card selecionado do Choice e do SegmentControl (boxShadow) e o gradiente do Badge freemium (experimental_backgroundImage) precisam do React Native 0.76+ com a nova arquitetura. Abaixo disso, o card fica sem sombra e o freemium fica com o fundo navy sólido.

Apps brownfield que já usam babel-plugin-module-resolver com aliases @/ apontando para o src/ do app também precisam de:

npm install -D babel-plugin-module-resolver

Setup brownfield (Expo + UI legada / Restyle)

O NativeWind se aplica apenas ao @medway-ui/native + telas/componentes que usam a biblioteca (ex.: src/pages/medway-ui/, features parcialmente migradas).

Implementação de referência: test-prep-app (metro.config.js, babel.config.js, tailwind.config.js, src/pages/medway-ui/).

1. metro.config.js

const { getDefaultConfig } = require("expo/metro-config");
const { withMedwayUi } = require("@medway-ui/native/metro");

module.exports = withMedwayUi(getDefaultConfig(__dirname), {
  projectRoot: __dirname,
  // linkedMedwayUiRoot: detectado automaticamente (npm ou monorepo)
});

| Cenário | linkedMedwayUiRoot | | --- | --- | | Pacotes npm (npm install) | Omita: detecta node_modules/@medway-ui automaticamente | | Clone local do medway-ui | "../medway-ui" (opcional; também detectado automaticamente) | | Symlink para <clone>/packages/native | Detectado automaticamente, qualquer que seja o nome da pasta do clone |

O padrão (resolveNativeSource: true) resolve para native/src e funciona com npm. Os arquivos compilados em lib/ (JS e .d.ts) usam imports relativos, sem aliases @/; os aliases só existem em src/, por isso apps com aliases @/ próprios continuam precisando do createMedwayUiAliasResolver (seção 3).

2. tailwind.config.js

const { createMedwayUiTailwindConfig } = require("@medway-ui/native/tailwind");

module.exports = createMedwayUiTailwindConfig({
  projectRoot: __dirname,
  content: [
    "./src/pages/medway-ui/**/*.{ts,tsx}",
    // adicione pastas extras que usam className + @medway-ui/native:
    // "./src/pages/MyFeature/**/*.{ts,tsx}",
  ],
});

createMedwayUiTailwindConfig já inclui os globs de node_modules/@medway-ui/native/src e carrega os tokens de @medway-ui/core (compatível com Node 22+).

3. babel.config.js

O preset padrão do app não deve habilitar o NativeWind globalmente. Use createBrownfieldOverride para restringir o NativeWind à biblioteca + pasta de telas migradas.

Se o app define aliases @/ para ./src/* via babel-plugin-module-resolver, você precisa redirecionar os imports @/ dos arquivos do @medway-ui/native para o código-fonte da biblioteca. Caso contrário, imports como @/components/Badge resolvem para o src/ do app e quebram o bundle.

const {
  createBrownfieldOverride,
  createMedwayUiAliasResolver,
} = require("@medway-ui/native/babel-brownfield");

const resolveMedwayUiAlias = createMedwayUiAliasResolver(__dirname);

module.exports = function (api) {
  api.cache(true);

  const appPlugins = [
    [
      "module-resolver",
      {
        root: ["."],
        extensions: [".ios.ts", ".android.ts", ".ts", ".tsx", ".js", ".jsx", ".json"],
        alias: {
          "@/components": "./src/components",
          "@/pages": "./src/pages",
          // ... outros aliases do app
        },
        resolvePath(sourcePath, currentFile) {
          return resolveMedwayUiAlias(sourcePath, currentFile);
        },
      },
    ],
    // outros plugins do app (sem reanimated aqui)
  ];

  return {
    presets: ["babel-preset-expo"],
    plugins: appPlugins,
    overrides: [
      createBrownfieldOverride({
        projectRoot: __dirname,
        appPagesDir: "medway-ui", // subpasta de src/pages com as telas NativeWind
        plugins: [
          // repita os plugins necessários no escopo do medway-ui
          "react-native-reanimated/plugin", // sempre por último
        ],
      }),
    ],
  };
};

Apps sem aliases @/ no Babel não precisam de createMedwayUiAliasResolver.

4. Provider + CSS

Envolva a subárvore que usa @medway-ui/native com MedwayUiProvider e importe o CSS uma única vez:

import { MedwayUiProvider } from "@medway-ui/native/setup";
import "@medway-ui/native/setup/global.css";

function MyScreen() {
  return (
    <MedwayUiProvider theme="system">
      {/* Button, CardNews, etc. */}
    </MedwayUiProvider>
  );
}

Dark mode: passe colorScheme="light" | "dark" quando o usuário escolheu um tema no app (Redux, Zustand, etc.):

<MedwayUiProvider colorScheme={appColorScheme}>
  {children}
</MedwayUiProvider>

Quando o app segue o tema do sistema, não passe colorScheme. Não derive o valor do useColorScheme do React Native: o NativeWind aplica o tema pelo Appearance (app inteiro), então enquanto um provider força um tema, o useColorScheme devolve esse tema e não o do sistema. Vale o pedido do provider montado por último; ao desmontar, volta o pedido anterior ou o tema do sistema.

MedwayUiProvider inclui o PortalHost (obrigatório para Select, Dialog, Popover, Tooltip).

Não importe @medway-ui/core/tokens.css diretamente: é incompatível com o NativeWind v4. Os tokens vêm de setup/global.css + setup/tokens.native.css.

5. Navigator automático (catálogo / telas isoladas)

Crie uma única vez src/pages/medway-ui/navigator.tsx:

import { createMedwayUiNavigator, MedwayUiProvider } from "@medway-ui/native/setup";
import "@medway-ui/native/setup/global.css";

const { MedwayUiNavigator } = createMedwayUiNavigator(
  require.context(".", true, /^\.\/[^/]+\/index\.tsx$/),
);

function MedwayUiRoot() {
  return (
    <MedwayUiProvider theme="system">
      <MedwayUiNavigator />
    </MedwayUiProvider>
  );
}

export { MedwayUiRoot, MedwayUiNavigator };

Registre no stack do app:

<Stack.Screen name="MedwayUi" component={MedwayUiRoot} options={{ headerShown: false }} />

Nova tela = nova pasta src/pages/medway-ui/MyScreen/index.tsx, sem registro manual de rota.

6. Usando componentes em telas existentes (mistura de Restyle + Medway UI)

Padrão usado no Content Hub e em features parcialmente migradas:

import { Button, CardNews } from "@medway-ui/native";
import { MedwayUiProvider } from "@medway-ui/native/setup";
import "@medway-ui/native/setup/global.css";

function ContentHubScreen() {
  return (
    <Box> {/* Restyle */}
      <MedwayUiProvider colorScheme={colorScheme}>
        <CardNews>{/* ... */}</CardNews>
      </MedwayUiProvider>
    </Box>
  );
}

Adicione a pasta da feature ao content do tailwind.config.js.


Desenvolvimento com o clone do medway-ui (file:../medway-ui/packages/native)

Desde outubro de 2026 o medway-ui é um monorepo: os pacotes ficam em packages/. Quem apontava para file:../medway-ui e file:../medway-ui/native troca para:

{
  "@medway-ui/core": "file:../medway-ui/packages/core",
  "@medway-ui/native": "file:../medway-ui/packages/native"
}

No clone, instale e builde uma vez com pnpm (npm i -g pnpm, se ainda não tiver):

cd ../medway-ui && pnpm install && pnpm build:lib && pnpm build:native

Metro e Babel detectam o clone automaticamente pelo caminho real do @medway-ui/native, mesmo com o clone em outra pasta. Depois de mudanças na biblioteca, rode pnpm build:native de novo se estiver testando a lib compilada.

Os ícones continuam vindo do @medway-ui/icons instalado no app, não do packages/icons do clone: o Metro resolve primeiro pelo node_modules do app. Um ícone que ainda não saiu no npm aparece como undefined no app (erro "Element type is invalid" em runtime, sem erro de build). Para usar um ícone novo, espere a PR de versão dos ícones ser mergeada e atualize o @medway-ui/icons no app.

Reinicie o Metro com cache limpo: npx expo start --clear.


Solução de problemas

| Erro | Causa | Correção | | --- | --- | --- | | Unable to resolve .../src/components/Badge | Alias @/ do app sobrescreve o @/ da lib | Adicione createMedwayUiAliasResolver no Babel | | Unable to resolve @/lib/medwayIcons | Bug no @medway-ui/[email protected] | Atualize para 1.1.2+ | | Unable to resolve ../../../../../src/... a partir de lib/module/ | resolveNativeSource: false | Remova a opção (use o padrão true) | | Tokens / tailwind não carregam (Node 22+) | require de .ts dentro de node_modules | Use @medway-ui/[email protected]+ (createMedwayUiTailwindConfig usa fallback com jiti) | | Estilos do NativeWind não aplicados | Arquivo fora do escopo brownfield | Mova para src/pages/medway-ui/ ou adicione um glob no tailwind | | Select/Dialog não abre | PortalHost ausente | Use MedwayUiProvider |


Setup greenfield (app 100% NativeWind)

Para apps novos, sem UI legada:

// babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      ["babel-preset-expo", { jsxImportSource: "nativewind" }],
      "nativewind/babel",
    ],
    plugins: ["react-native-reanimated/plugin"],
  };
};
// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { withMedwayUi } = require("@medway-ui/native/metro");

module.exports = withMedwayUi(getDefaultConfig(__dirname), {
  projectRoot: __dirname,
});

Importe @medway-ui/native/setup/global.css no layout raiz.


Uso

import {
  Alert,
  AlertContainer,
  AlertContent,
  AlertTitle,
  AlertDescription,
  Badge,
  Button,
  Input,
} from "@medway-ui/native";

export default function Screen() {
  return (
    <>
      <Button variant="regular" size="md" onPress={() => {}}>
        Continuar
      </Button>

      <Input
        label="E-mail"
        type="email"
        size="lg"
        value={email}
        onChangeText={setEmail}
        placeholder="[email protected]"
      />

      <Alert variant="success" onClose={() => {}}>
        <AlertContainer>
          <AlertContent>
            <AlertTitle>Pronto</AlertTitle>
            <AlertDescription>Operação concluída com sucesso.</AlertDescription>
          </AlertContent>
        </AlertContainer>
      </Alert>

      <Badge variant="regular" size="md">9+</Badge>
    </>
  );
}

Ícones

O pacote @medway-ui/icons inclui um entry point nativo:

import { Check, CloseMd } from "@medway-ui/icons/native";

Componentes: status

| Componente | Status | Notas | | --- | --- | --- | | Button | ✅ | Variantes, loading, badge, ponto de alerta | | Input | ✅ | text, email, password, numeric | | Alert | ✅ | Subcomponentes, todas as variantes | | Badge | ✅ | Primary, secondary e grandes áreas, cheias ou -soft; freemium, só ícone e ponto | | Loading | ✅ | Spin com Reanimated | | LoadingOverlay | ✅ | Overlay de Card + Modal, ícone central customizável | | Checkbox | ✅ | @rn-primitives/checkbox | | RadioGroup | ✅ | @rn-primitives/radio-group | | Switch | ✅ | @rn-primitives/switch | | Select | ✅ | @rn-primitives/select | | Label | ✅ | Pressable + Text | | Separator | ✅ | View de 1px | | Dialog | ✅ | @rn-primitives/dialog | | AlertDialog | ✅ | @rn-primitives/alert-dialog | | Sheet | ✅ | @rn-primitives/dialog (ciente do lado) | | Tag | ✅ | 16+ variantes | | Toast | ✅ | useToast + toast(); variantes com gradiente + inset de safe area | | Tooltip | ✅ | @rn-primitives/tooltip | | TextMarker | ✅ | Toolbar do marca-texto ancorada a um retângulo da janela; textMarkerMarkVariants para o <Text> | | Popover | ✅ | @rn-primitives/popover | | Skeleton | ✅ | Shimmer | | Avatar | ✅ | @rn-primitives/avatar | | FallbackImage | ✅ | react-native-svg | | CardNews | ✅ | Card composable (Content Hub) | | IndexLevel | ✅ | Níveis p/h/m/l, variantes, anel com gradiente animated | | Tabs | ✅ | Tabs, TabsList, TabsTrigger, TabsContent | | SegmentControl | ✅ | SegmentControl, SegmentControlList, SegmentControlTrigger (ícone e badge), SegmentControlContent | | Chip | ✅ | Tamanhos e variantes | | Choice | ✅ | Pill selecionável com cores do tema | | Filter | ✅ | Pill com critérios e indicador de dropdown | | Text | ✅ | Resolução da fonte Montserrat | | ThemedIcon | ✅ | Ícone colorido por token do tema | | DropdownMenu | ✅ | @rn-primitives/dropdown-menu | | Carousel | ✅ | Slides de mídia e de métrica | | EmptyState | ✅ | Placeholder de lista/tela vazia | | Feed | ✅ | Feed, FeedTitle, FeedComposer | | FeedNotice | ✅ | Banner de aviso do feed | | Feedback | ✅ | Card de feedback | | InvitePopover | ✅ | Popover de convite composable | | LeaderboardCard | ✅ | Cabeçalho e lista ranqueada | | MemberRow | ✅ | Linha de membro com indicador de posição | | PageHeader | ✅ | Cabeçalho de tela | | PostForm | ✅ | Formulário de post composable (mídia, data/hora, opções) | | PrizeCollection | ✅ | Estatísticas, prateleira, linhas e itens | | SectionHeader | ✅ | Cabeçalho com título de seção | | Progress | 🔜 | @rn-primitives/progress | | ProgressCircle | 🔜 | react-native-svg | | Cards | 🔜 | CardModule, CardLesson, etc. | | DatePicker | 🔜 | @react-native-community/datetimepicker |