Skip to main content
Glama
platform-differences.md5.42 kB
--- title: Handle platform differences description: In this tutorial, learn how to handle platform differences between native and web when creating a universal app. hasVideoLink: true --- Android, iOS, and the web have different capabilities. In our case, both Android and iOS can capture a screenshot with the `react-native-view-shot` library. However, web browsers cannot. In this chapter, we'll learn how to handle capturing screenshots for web browsers so our app has the same functionality on all platforms. --- ## Install and import dom-to-image To capture a screenshot on the web and save it as an image, we'll use a third-party library called [`dom-to-image`](https://github.com/tsayen/dom-to-image#readme). It takes a screenshot of any DOM node and turns it into a vector (SVG) or raster (PNG or JPEG) image. Stop the development server and run the following command to install the library: After installing it, make sure to restart the development server and press <kbd>w</kbd> in the terminal. ## Add platform-specific code Using `Platform` module from React Native, we can implement platform-specific behavior. Inside **app/(tabs)/index.tsx**: 1. Import the `Platform` module from `react-native`. 2. Import the `domtoimage` library from `dom-to-image`. 3. Update the `onSaveImageAsync()` function to check whether the current platform is `'web'` with the `Platform.OS` property. If it is `'web'`, we'll use the `domtoimage.toJpeg()` method to convert and capture the current `<View>` as a JPEG image. Otherwise, we'll keep using the same logic added for native platforms. {/* prettier-ignore */} ```tsx app/(tabs)/index.tsx /* @tutinfo Import <CODE>domtoimage</CODE> library. */import domtoimage from 'dom-to-image';/* @end */ const PlaceholderImage = require('@/assets/images/background-image.png'); const [selectedImage, setSelectedImage] = useState<string | undefined>(undefined); const [showAppOptions, setShowAppOptions] = useState<boolean>(false); const [isModalVisible, setIsModalVisible] = useState<boolean>(false); const [pickedEmoji, setPickedEmoji] = useState<ImageSourcePropType | undefined>(undefined); const [status, requestPermission] = MediaLibrary.usePermissions(); const imageRef = useRef<View>(null); if (status === null) { requestPermission(); } const pickImageAsync = async () => { let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsEditing: true, quality: 1, }); if (!result.canceled) { setSelectedImage(result.assets[0].uri); setShowAppOptions(true); } else { alert('You did not select any image.'); } }; const onReset = () => { setShowAppOptions(false); }; const onAddSticker = () => { setIsModalVisible(true); }; const onModalClose = () => { setIsModalVisible(false); }; const onSaveImageAsync = async () => { /* @tutinfo Add the if condition here to check whether the current platform is web or not. */ if (Platform.OS !== 'web') { /* @end */ try { const localUri = await captureRef(imageRef, { height: 440, quality: 1, }); await MediaLibrary.saveToLibraryAsync(localUri); if (localUri) { alert('Saved!'); } } catch (e) { console.log(e); } /* @tutinfo Add an else condition to run the logic when the current platform is the web. */ } else { try { const dataUrl = await domtoimage.toJpeg(imageRef.current, { quality: 0.95, width: 320, height: 440, }); let link = document.createElement('a'); link.download = 'sticker-smash.jpeg'; link.href = dataUrl; link.click(); } catch (e) { console.log(e); } } /* @end */ }; return ( <GestureHandlerRootView style={styles.container}> <View style={styles.imageContainer}> <View ref={imageRef} collapsable={false}> {pickedEmoji && } </View> </View> {showAppOptions ? ( <View style={styles.optionsContainer}> <View style={styles.optionsRow}> </View> </View> ) : ( <View style={styles.footerContainer}> <Button label="Use this photo" onPress={() => setShowAppOptions(true)} /> </View> )} <EmojiPicker isVisible={isModalVisible} onClose={onModalClose}> </EmojiPicker> </GestureHandlerRootView> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#25292e', alignItems: 'center', }, imageContainer: { flex: 1, }, footerContainer: { flex: 1 / 3, alignItems: 'center', }, optionsContainer: { position: 'absolute', bottom: 80, }, optionsRow: { alignItems: 'center', flexDirection: 'row', }, }); ``` <Collapsible summary={<>Fix <CODE>dom-to-image</CODE> TypeScript module error</>}> We need to add a type definition after importing the `domtoimage` library since we're using TypeScript. We can do this by creating a file **types.d.ts** in the root of our project directory and adding the declaration statement: ```tsx types.d.ts declare module 'dom-to-image'; ``` </Collapsible> On running the app in a web browser, we can now save a screenshot: ## Summary

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jaksm/expo-docs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server