DEV Community

Krzysztof Jackowski for Netguru

Posted on

Stickyheader.js - beautiful headers in React Native

Stickyheader.js

Introduction

Stickyheader.js is a simple React Native library, enabling to create a fully custom header for your iOS and Android apps.

Preview

Features

Stickyheader.js ships with 3 different use cases for sticky headers and a possibility to create fully custom header!

Tabbed Header Avatar Header Details Header
Tabbed Header Avatar Header Gif Details Header Gif

In Use

Predefined headers can be accessed through headerType="HeaderName" property, each header can be configured according to your demands using the wide amount of properties. You can change all of them, or use it right out of the box with as little changes as possible to use it for you needs:)

This is how you can add them in your app:

import React from 'react'
import StickyParalaxHeader from 'react-native-sticky-parallax-header'

const TestScreen = () => (
  <React.Fragment>
    <StickyParalaxHeader headerType="TabbedHeader" />
    {/* <StickyParalaxHeader headerType="AvatarHeader" /> */}
    {/* <StickyParalaxHeader headerType="DetailsHeader" /> */}
  </React.Fragment>
)

export default TestScreen
Enter fullscreen mode Exit fullscreen mode

Below are examples of those components and description of the props they are accepting.

Tabbed Header

Tabbed Header Gif

Property Type Optional Default Description
backgroundColor string Yes #1ca75d Header background color
headerHeight number Yes ifIphoneX(92, constants.responsiveHeight(13)) Sets height of folded header
backgroundImage number Yes null Sets header background image
title string Yes "Mornin' Mark! \nReady for a quiz?" Sets header title
bounces bool Yes true Bounces on swiping up
snapToEdge bool Yes true Boolean to fire the function for snap To Edge
renderBody func Yes title => <RenderContent title={title} /> Function that renders body of the header (can be empty)
tabs arrayOf(shape({})) Yes [{title: 'Popular',content: <RenderContent title="Popular Quizes" />},...], Array with tabs names and content

Details Header

Details Header Gif

Property Type Optional Default Description
leftTopIconOnPress func Yes () => {} Define action on left top button press
rightTopIconOnPress func Yes () => {} Define action on right top button press
leftTopIcon number Yes require('../../assets/icons/iconCloseWhite.png') Set icon for left top button
rightTopIcon number Yes require('../../assets/icons/Icon-Menu.png') Set icon for right top button
backgroundColor string Yes #1ca75d Header background color
headerHeight number Yes ifIphoneX(92, constants.responsiveHeight(13)) Sets height of folded header
backgroundImage number Yes null Sets header background image
tag string Yes "Product Designer" Sets header tag name
title string Yes "Design System" Sets header title
image number Yes require('../../assets/images/photosPortraitBrandon.png') Sets header image
renderBody func Yes title => <RenderContent title={title} /> Function that renders body of the header (can be empty)
bounces bool Yes true Bounces on swiping up
snapToEdge bool Yes true Boolean to fire the function for snap To Edge
hasBorderRadius bool Yes true Adds radius to header's left bottom border
iconNumber number Yes 10 Set amount of cards shown on icon

Avatar Header

Avatar Header Gif

Property Type Optional Default Description
leftTopIconOnPress func Yes () => {} Define action on left top button press
rightTopIconOnPress func Yes () => {} Define action on right top button press
leftTopIcon number Yes require('../../assets/icons/iconCloseWhite.png') Set icon for left top button
rightTopIcon number Yes require('../../assets/icons/Icon-Menu.png') Set icon for right top button
backgroundColor string Yes #1ca75d Header background color
headerHeight number Yes ifIphoneX(92, constants.responsiveHeight(13)) Sets height of folded header
backgroundImage number Yes null Sets header background image
title string Yes "Brandon Sets header title
subtitle string Yes "Coffee buff. Web enthusiast. Unapologetic student. Gamer. Avid organizer." Sets description(subtitle) section
image number Yes require('../../assets/images/photosPortraitBrandon.png') Sets header image
renderBody func Yes title => <RenderContent title={title} /> Function that renders body of the header (can be empty)
bounces bool Yes true Bounces on swiping up
snapToEdge bool Yes true Boolean to fire the function for snap To Edge
hasBorderRadius bool Yes true Adds radius to header's left bottom border

Custom Header

Property Type Required Default Description
background node No - This renders background component
backgroundImage number No - This renders background image instead of background component
backgroundColor string Yes "" Header background color
bounces bool Yes true Bounces on swiping up
children node No - This renders all the children inside the component
foreground node Yes - This renders foreground component
header node Yes - This renders header component
headerHeight number No 92 Sets height of folded header
headerSize func No - Returns size of header for current device
initialPage number No 0 Set initial page of tab bar
onChangeTab func No - Tab change event
onEndReached func No - Tab change event
parallaxHeight number No 0 Sets height of opened header
snapToEdge bool No true Boolean to fire the function for snap To Edge
scrollEvent func No - Returns offset of header to apply custom animations
tabs arrayOf(string) No - Array of tab names
tabTextStyle shape({}) No {} Text styles of tab
tabTextActiveStyle shape({}) No {} Text styles of active tab
tabTextContainerStyle shape({}) No {} Container styles of tab
tabTextContainerActiveStyle shape({}) No {} Container styles of active tab
tabsContainerBackgroundColor string No - Background color of tab bar container
tabsWrapperStyle shape({}) No {} Tabs Wrapper styles

Getting Started

Prerequisites

Installation

$ yarn add react-native-sticky-parallax-header
Enter fullscreen mode Exit fullscreen mode

In order to make tab bar work, we have to link react-native-nested-scroll-view package.

$ react-native link react-native-nested-scroll-view
Enter fullscreen mode Exit fullscreen mode

Depending on the version of React Native you use, the package can be still making issues for you, you have to install patch-package

$ yarn add patch-package postinstall-postinstall
Enter fullscreen mode Exit fullscreen mode

Then you add this script to your scripts:

 "scripts": {
+  "postinstall": "patch-package"
 }
Enter fullscreen mode Exit fullscreen mode

After all those steps, just copy a 'patches' folder from this repository and run yarn again to apply the patched package.
You're ready to use the package now.

Running/Development

  1. iOS:
$ react-native run-ios
Enter fullscreen mode Exit fullscreen mode
  1. Android:
$ react-native run-android
Enter fullscreen mode Exit fullscreen mode

Running Tests

$ yarn test
Enter fullscreen mode Exit fullscreen mode

Creating new Pull Request

  • remember to add appropriate title, ticket, description
  • adding video or screenshot is very beneficial but it's not mandatory
  • additionally please remember to add appropriate Pull Request title from following:
    • [RNS-XX] short description - for normal feature branches

Code structure

src/
├──assets
├──components
├──constants
├──predefinedComponents
Enter fullscreen mode Exit fullscreen mode

Code Style

  • Make sure you are using linter with linting rules defined in ESLint config (.eslinrc)
  • Name branch according to your ticket following this pattern: RNS-XX-short_description
  • Imports and exports inside index.js files eg. screens/index.js, components/index.js should be alfabetically
  • Style names in ComponentName.styles.js should be ordered alfabetically

Usage

Here is a basic example of how to create a custom header

import React from 'react'
import { Text, View, Animated, StyleSheet } from 'react-native'
import StickyParallaxHeader from 'react-native-sticky-parallax-header'

const styles = StyleSheet.create({
  content: {
    height: 1000,
    marginTop: 50
  },
  foreground: {
    flex: 1,
    justifyContent: 'flex-end'
  },
  message: {
    color: 'white',
    fontSize: 40,
    paddingTop: 24,
    paddingBottom: 7
  },
  headerWrapper: {
    backgroundColor: 'green',
    width: '100%',
    paddingHorizontal: 24,
    paddingBottom: 25,
    flexDirection: 'row',
    alignItems: 'center'
  },
  headerTitle: {
    fontSize: 16,
    color: 'white',
    margin: 12
  },
  tabsWrapper: {
    paddingVertical: 12
  },
  tabTextContainerStyle: {
    backgroundColor: 'transparent',
    borderRadius: 18
  },
  tabTextContainerActiveStyle: {
    backgroundColor: 'lightgreen'
  },
  tabText: {
    fontSize: 16,
    lineHeight: 20,
    paddingHorizontal: 12,
    paddingVertical: 8,
    color: 'white'
  }
})

class TabScreen extends React.Component {
  state = {
    scroll: new Animated.Value(0)
  }

  componentDidMount() {
    const { scroll } = this.state
    scroll.addListener(({ value }) => (this._value = value))
  }

  renderContent = (label) => (
    <View style={styles.content}>
      <Text>{label}</Text>
    </View>
  )

  renderForeground = () => {
    const { scroll } = this.state
    const titleOpacity = scroll.interpolate({
      inputRange: [0, 106, 154],
      outputRange: [1, 1, 0],
      extrapolate: 'clamp'
    })

    return (
      <View style={styles.foreground}>
        <Animated.View style={{ opacity: titleOpacity }}>
          <Text style={styles.message}>STICKY HEADER</Text>
        </Animated.View>
      </View>
    )
  }

  renderHeader = () => {
    const { scroll } = this.state
    const opacity = scroll.interpolate({
      inputRange: [0, 160, 210],
      outputRange: [0, 0, 1],
      extrapolate: 'clamp'
    })

    return (
      <View style={styles.headerWrapper}>
        <Animated.View style={{ opacity }}>
          <Text style={styles.headerTitle}>STICKY HEADER</Text>
        </Animated.View>
      </View>
    )
  }

  render() {
    const { scroll } = this.state

    return (
      <StickyParallaxHeader
        foreground={this.renderForeground()}
        header={this.renderHeader()}
        parallaxHeight={200}
        headerHeight={90}
        headerSize={() => {}}
        onEndReached={() => {}}
        scrollEvent={Animated.event([{ nativeEvent: { contentOffset: { y: scroll } } }])}
        tabs={[
          {
            title: 'First Tab',
            content: this.renderContent('FIRST TAB')
          },
          {
            title: 'Second Tab',
            content: this.renderContent('SECOND TAB')
          },
          {
            title: 'Third Tab',
            content: this.renderContent('THIRD TAB')
          },
          {
            title: 'Fourth Tab',
            content: this.renderContent('FOURTH TAB')
          },
          {
            title: 'Fifth Tab',
            content: this.renderContent('FIFTH TAB')
          }
        ]}
        tabTextStyle={styles.tabText}
        tabTextContainerStyle={styles.tabTextContainerStyle}
        tabTextContainerActiveStyle={styles.tabTextContainerActiveStyle}
        tabsContainerBackgroundColor={'green'}
        tabsWrapperStyle={styles.tabsWrapper}
      >
      </StickyParallaxHeader>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

Demo

Live demo videos

If you want to test package without installation you can download our showcase app

Tips

In order to nest scrollable component use scrollEnabled={false} on it and move all the logic to the header eg. by using onEndReached prop.

Technology stack

Technology Version
React 16.8.6
React Native 0.59.8

License

The gem is available as open source under the terms of the MIT License.

Top comments (8)

Collapse
 
diogojefs profile image
diogojefs

Perform all the steps without problems, but when I run react-native run-android I get:

Task: react-native-nested-scroll-view: processDebugManifest FAILED

FAILURE: Build failed with an exception.

What went wrong: Failed to create directory 'C: \ Users \ Diogo \ Documents \ Mobile Projects \ newproject \ node_modules \ react-native-nested-scroll-view \ android \ build \ intermediates \ merged_manifests \ debug' > root project> Resolve dependencies of: classpath
Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. > IDLE
Get more help at help.gradle.org

I wear:
react-native-cli: 2.0.1
react-native: 0.59.8
node: v10.13.0

Collapse
 
diogojefs profile image
diogojefs
Collapse
 
diogojefs profile image
diogojefs

Perfect! is it possible to use with the expo?

Collapse
 
idaszakdaniel profile image
Daniel Idaszak

Unfortunately, this library is for native code projects only - we used nested scrollview library which has to be linked. Currently, we are investigating if It's possible to use It with Expo, but without scrollable menu in the header

Collapse
 
diogojefs profile image
diogojefs

thanks!

Collapse
 
sendypw profile image
Sendy Putra

awesome!

Collapse
 
partitect profile image
partitect

hi this look awesome, but showcase app dont work.

Collapse
 
giraaaaaa profile image
giraaaaaa

hello
i have some problem

  1. i use foreground without safearea but i want use safearea on tabs but it doesnt work
  2. snapToEdge is too hard i want to be more soft snap

how can i fix it?