Skip to content

A Lightweight Storybook Alternative for Effortless View Design.

License

Notifications You must be signed in to change notification settings

murat-mehmet/react-native-design-mode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

38 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ› οΈ React Native Design Mode - Simplify Creating Views

npm version License: MIT

A Lightweight Storybook Alternative for Effortless View Design.

Table of Contents

  1. πŸ“„ Description
  2. πŸš€ Features
  3. βš™ Installation
  4. 🏁 Getting Started
  5. πŸ’‘ Usage
  1. βš™ Configuration
  1. πŸ‘Š Contributing
  2. πŸ“› License
  3. πŸ“§ Contact

πŸ“„ Description

React Native Design Mode is a developer-friendly package that streamlines the process of creating views without the need to navigate through the entire app flow. If you've found Storybook to be a bit heavy for your simple projects, this package offers a lightweight alternative with all the features you need.

Heavily inspired by storybook project so most things are similar.

πŸš€ Features

  • Effortless design mode toggle with a floating button
  • Allows developer to focus more on quickly designing views
  • Lightweight and efficient implementation
  • Familiar structure that is inspired by Storybook

βš™ Installation

You can install the package via npm or yarn:

npm install react-native-design-mode
# or
yarn add react-native-design-mode

🏁 Getting Started

Run CLI to quickly install designer into your project.

designer install

It does two things:

  1. Creates a .designer folder in your project root with required files in it.
  2. Adds designer update command to the prestart script.
  • Note: You can use -c flag to change the designer folder. Run designer help install for details.

After running the command you will have the following folder structure

β”œβ”€β”€ project root
β”‚   └── πŸ“ .designer
β”‚       β”œβ”€β”€ πŸ“„ designer.requires.js
β”‚       β”œβ”€β”€ πŸ“„ index.js
β”‚       β”œβ”€β”€ πŸ“„ main.js
β”‚       └── πŸ“„ preview.js

πŸ’‘ Usage

Import the designer file

import Designer from "./.designer";

Add Designer component in your App.tsx

const App = () => {
  return (
    <>
      <SafeAreaProvider>
        <AppContainer />
      </SafeAreaProvider>
      <Designer />
    </>
  );
}

Or use it directly in your index.js file

AppRegistry.registerComponent(appName, () => Designer);

Now you are ready to create your first design!

🎨 Creating designs

Imagine you have a Login.tsx screen that you want to design.

To achieve this, all you have to do is create a new file named Login.design.tsx right next to the Login.tsx.

// src/Login.design.tsx

import Login from './Login';

export default {
  title: 'Login',
  component: Login,
}

Once you've added the new design file, you'll need to update the design list.

You can do this in two ways:

  • Use the command-line interface (CLI) to manually update the list. (Recommended)
designer update
  • Restart the bundler, which will automatically trigger the prestart script
npm start

Great news! You're all set! Open the app, and you'll see the Login screen right in design mode. Now you can start designing and refining the layout with ease.

πŸ”„ Moving between designs

While we are on the design selection screen, floating button works like a toggle to switch design mode on/off.

After you select a design, we can click on floating button to go back to design list.

πŸ”§ Creating designs with variants

We can easily create different variants of a design by exporting more design objects.

// src/Login.design.tsx

import Login from './Login';

export default {
  title: 'Login',
  component: Login,
}

// export default variant
export const Basic = {}

// export another variant
export const Dark = () => <Login theme={'dark'} />

πŸ“ Categorized list view

We can categorize our views infinitely by adding group names in titles

// src/Login.design.tsx

import Login from './Login';

export default {
  title: 'Authentication/Login',
  component: Login,
}

🎈 Draggable floating button

We have the flexibility to position the floating button on the screen edges, ensuring it won't obstruct our view design process.

  • Note: To make the floating button draggable on android, make sure you use either GestureHandlerRootView or gestureHandlerRootHOC. Check installation docs for RNGH

βš™ Configuration

πŸ“„ main.js

module.exports = {
  designs: ["../src/**/*.design.@(js|jsx|ts|tsx)"],
};
  • designs: This is an array that defines the location of our design files. By default, we use a glob pattern to load all *.design.js files inside the src directory and its subdirectories. You may need to adjust the path based on your project structure.
  • excludePaths: This is an array of path patterns that will be excluded from loading. By default it is ['**/node_modules']

πŸ“„ index.js

import { getDesignModeUI } from "react-native-design-mode";

if (__DEV__) {
  require('./designer.requires');
}

const Designer = getDesignModeUI();

export default Designer;

All designs are imported here. Designs are stripped out by bundler when building for production so you dont have to worry about it.

πŸ“„ designer.requires.js

/* do not change this file, it is auto generated by design mode. */

import { context } from "react-native-design-mode";

context.initialize();
// ...

This is an auto generated file by design update command which will be automatically executed on every npm run start.

It is imported in .designer/index.js and contains all designs of your project.

You shouldn't modify this file as it will be overwritten.

πŸ“„ preview.js

import React from "react";
import { SafeAreaProvider } from "react-native-safe-area-context";

export const decorators = [
  // Add SafeAreaProvider to all designs
  (Component, ctx) => (
    <SafeAreaProvider>
      <Component />
    </SafeAreaProvider>
  ),
];

This file is completely optional but it will help us create global declarations for all of our designs.

  • decorators: This is an array of functions of Elements or Components that will be applied on every design. We can use it to create wrappers like SafeAreaProvider or Navigator

Check this example where we apply navigator conditionally.

// .designer/preview.js

const Stack = createStackNavigator();

export const decorators = [
  (Component, ctx) => {
    if (ctx.parameters.withNavigation)
      return (
        <NavigationContainer>
          <Stack.Navigator initialRouteName="design" screenOptions={{headerShown: false}}>
            <Stack.Screen name="design" component={Component} />
          </Stack.Navigator>
        </NavigationContainer>
      );
    return Component;
  },
];

// src/Login.design.tsx

export default {
  title: "Login",
  component: Login,
  parameters: {
    withNavigation: true
  }
};
  • parameters: Global parameters that will be applied on all designs.

Parameter: designer

This is a special parameter which can config features of our designer. Currently there is only one feature:

Theme Switcher

To enable theme switcher feature, add a themeSwitcher parameter like in example:

export const parameters = {
  designer: {
    themeSwitcher: {
      set: (ctx, value) => {
        // here you can set your theme
        // value is true for dark, false for light
      },
      value: false, // initial value
    }
  }
}
  • loaders: This is an object of promise functions that will be executed before displaying any design. The loaded results can be accessed from ctx.loaded field.

Example

// .designer/preview.js

export const loaders = {
  stores: async () => getStores(),
};

// example of accesing "loaded" field in decorators
export const decorators = [
  (Component, ctx) => {
      return (
        <Provider {...ctx.loaded.stores}> {/* <-- */} 
          <Component />
        </Provider>
      );
  },
];

// src/Login.design.tsx

// example of accessing "loaded" field in designs
export default {
  title: "Login",
  component: Login,
  prepare: (ctx) => { 
    // Do something with ctx.loaded.stores 
  }
};

πŸ‘Š Contributing

Contributions are welcome! If you find a bug or want to add a new feature, feel free to submit a pull request. For major changes, please open an issue first to discuss the proposed changes.

πŸ“› License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ“§ Contact

If you have any questions or need assistance, feel free to open an issue.

Happy coding!