diff --git a/src/generators.js b/src/generators.js
index 91247c1d..6e6cbdf4 100644
--- a/src/generators.js
+++ b/src/generators.js
@@ -3,6 +3,7 @@ import NextGenerator from "./generators/NextGenerator.js";
 import NuxtGenerator from "./generators/NuxtGenerator.js";
 import ReactGenerator from "./generators/ReactGenerator.js";
 import ReactNativeGenerator from "./generators/ReactNativeGenerator.js";
+import ReactNativeGeneratorV2 from "./generators/ReactNativeGeneratorV2.js";
 import TypescriptInterfaceGenerator from "./generators/TypescriptInterfaceGenerator.js";
 import VueGenerator from "./generators/VueGenerator.js";
 import VuetifyGenerator from "./generators/VuetifyGenerator.js";
@@ -28,6 +29,8 @@ export default async function generators(generator = "react") {
       return wrap(ReactGenerator);
     case "react-native":
       return wrap(ReactNativeGenerator);
+    case "react-native-v2":
+      return wrap(ReactNativeGeneratorV2);
     case "typescript":
       return wrap(TypescriptInterfaceGenerator);
     case "vue":
diff --git a/src/generators/ReactNativeGeneratorV2.js b/src/generators/ReactNativeGeneratorV2.js
new file mode 100644
index 00000000..3d934619
--- /dev/null
+++ b/src/generators/ReactNativeGeneratorV2.js
@@ -0,0 +1,177 @@
+import chalk from "chalk";
+import handlebars from "handlebars";
+import BaseGenerator from "./BaseGenerator.js";
+import hbhComparison from "handlebars-helpers/lib/comparison.js";
+
+export default class extends BaseGenerator {
+  constructor(params) {
+    super(params);
+
+    handlebars.registerHelper("ifNotResource", function (item, options) {
+      if (item === null) {
+        return options.fn(this);
+      }
+      return options.inverse(this);
+    });
+
+    this.registerTemplates(`react-native-v2/`, [
+      "app/(tabs)/foos.tsx",
+      "app/_layout.tsx.dist",
+      "lib/hooks/data.ts",
+      "lib/hooks/mercure.ts",
+      "lib/hooks/modal.ts",
+      "lib/hooks/notifications.ts",
+      "lib/types/ApiResource.ts",
+      "lib/types/HydraView.ts",
+      "lib/types/HydraResponse.ts",
+      "lib/types/foo.ts",
+      "lib/utils/Logs.ts",
+      "lib/utils/mercure.ts",
+      "lib/utils/icons.tsx",
+      "lib/api/fooApi.ts",
+      "components/Main.tsx",
+      "components/Navigation.tsx",
+      "components/ConfirmModal.tsx",
+      "components/foo/CreateEditModal.tsx",
+      "components/foo/Form.tsx",
+      "components/foo/LogsRenderer.tsx",
+      "components/foo/Context.ts",
+    ]);
+
+    handlebars.registerHelper("compare", hbhComparison.compare);
+  }
+
+  help(resource) {
+    const titleLc = resource.title.toLowerCase();
+
+    console.log(
+      'Code for the "%s" resource type has been generated!',
+      resource.title
+    );
+
+    console.log(
+      "You should replace app/_layout.tsx by the generated one and add the following route:"
+    );
+    console.log(
+      chalk.green(`
+            
+
+            tabs: {
+                ...
+                ${titleLc}: {
+                    title: '${titleLc}',
+                    headerShown: false,
+                    tabBarIcon: ({ color }) => ,
+                },
+            }
+            `)
+    );
+  }
+
+  generate(api, resource, dir) {
+    const lc = resource.title.toLowerCase();
+    const titleUcFirst =
+      resource.title.charAt(0).toUpperCase() + resource.title.slice(1);
+    const fields = this.parseFields(resource);
+
+    const context = {
+      title: resource.title,
+      name: resource.name,
+      lc,
+      uc: resource.title.toUpperCase(),
+      fields,
+      formFields: this.buildFields(fields),
+      hydraPrefix: this.hydraPrefix,
+      ucf: titleUcFirst,
+    };
+
+    // Create directories
+    // These directories may already exist
+    [
+      `${dir}/app/(tabs)`,
+      `${dir}/config`,
+      `${dir}/components`,
+      `${dir}/components/${lc}`,
+      `${dir}/lib`,
+      `${dir}/lib/api`,
+      `${dir}/lib/types`,
+      `${dir}/lib/hooks`,
+      `${dir}/lib/utils`,
+    ].forEach((dir) => this.createDir(dir, false));
+
+    // static files
+    [
+      "lib/types/ApiResource.ts",
+      "lib/hooks/data.ts",
+      "lib/hooks/mercure.ts",
+      "lib/hooks/modal.ts",
+      "lib/hooks/notifications.ts",
+      "lib/utils/Logs.ts",
+      "lib/utils/mercure.ts",
+      "lib/utils/icons.tsx",
+      "components/Main.tsx",
+      "components/Navigation.tsx",
+      "components/ConfirmModal.tsx",
+    ].forEach((file) => this.createFile(file, `${dir}/${file}`));
+
+    // templated files ucFirst
+    ["lib/types/%s.ts"].forEach((pattern) =>
+      this.createFileFromPattern(pattern, dir, [titleUcFirst], context)
+    );
+
+    // templated files lc
+    [
+      "app/(tabs)/%ss.tsx",
+      "app/_layout.tsx.dist",
+      "lib/api/%sApi.ts",
+      "components/%s/Context.ts",
+      "components/%s/CreateEditModal.tsx",
+      "components/%s/Form.tsx",
+      "components/%s/LogsRenderer.tsx",
+      "lib/types/HydraView.ts",
+      "lib/types/HydraResponse.ts",
+    ].forEach((pattern) =>
+      this.createFileFromPattern(pattern, dir, [lc], context)
+    );
+
+    this.createEntrypoint(api.entrypoint, `${dir}/config/entrypoint.js`);
+  }
+
+  getDescription(field) {
+    return field.description ? field.description.replace(/"/g, "'") : "";
+  }
+
+  parseFields(resource) {
+    const fields = [
+      ...resource.writableFields,
+      ...resource.readableFields,
+    ].reduce((list, field) => {
+      if (list[field.name]) {
+        return list;
+      }
+
+      const isReferences = Boolean(
+        field.reference && field.maxCardinality !== 1
+      );
+      const isEmbeddeds = Boolean(field.embedded && field.maxCardinality !== 1);
+
+      return {
+        ...list,
+        [field.name]: {
+          ...field,
+          type: this.getType(field),
+          description: this.getDescription(field),
+          readonly: false,
+          isReferences,
+          isEmbeddeds,
+          isRelations: isEmbeddeds || isReferences,
+        },
+      };
+    }, {});
+
+    return Object.values(fields);
+  }
+}
diff --git a/src/generators/ReactNativeGeneratorV2.test.js b/src/generators/ReactNativeGeneratorV2.test.js
new file mode 100644
index 00000000..1c0626b9
--- /dev/null
+++ b/src/generators/ReactNativeGeneratorV2.test.js
@@ -0,0 +1,67 @@
+import { Api, Resource, Field } from "@api-platform/api-doc-parser";
+import path from "path";
+import { fileURLToPath } from "url";
+import fs from "fs";
+import tmp from "tmp";
+import ReactNativeGeneratorV2 from "./ReactNativeGeneratorV2.js";
+
+const dirname = path.dirname(fileURLToPath(import.meta.url));
+
+test("Generate a React Native V2 app", () => {
+  const generator = new ReactNativeGeneratorV2({
+    hydraPrefix: "hydra:",
+    templateDirectory: `${dirname}/../../templates`,
+  });
+  const tmpobj = tmp.dirSync({ unsafeCleanup: true });
+
+  const fields = [
+    new Field("bar", {
+      id: "http://schema.org/url",
+      range: "http://www.w3.org/2001/XMLSchema#string",
+      reference: null,
+      required: true,
+      description: "An URL",
+    }),
+  ];
+  const resource = new Resource("abc", "http://example.com/foos", {
+    id: "abc",
+    title: "abc",
+    readableFields: fields,
+    writableFields: fields,
+  });
+  const api = new Api("http://example.com", {
+    entrypoint: "http://example.com:8080",
+    title: "My API",
+    resources: [resource],
+  });
+  generator.generate(api, resource, tmpobj.name);
+
+  [
+    "/lib/types/ApiResource.ts",
+    "/lib/types/HydraView.ts",
+    "/lib/types/HydraResponse.ts",
+    "/lib/utils/Logs.ts",
+    "/lib/utils/icons.tsx",
+    "/lib/utils/Logs.ts",
+    "/lib/utils/mercure.ts",
+    "/lib/hooks/data.ts",
+    "/lib/hooks/mercure.ts",
+    "/lib/hooks/modal.ts",
+    "/lib/hooks/notifications.ts",
+    "/config/entrypoint.js",
+    "/components/Main.tsx",
+    "/components/Navigation.tsx",
+    "/components/ConfirmModal.tsx",
+    "/app/_layout.tsx.dist",
+
+    "/app/(tabs)/abcs.tsx",
+    "/lib/api/abcApi.ts",
+    "/lib/types/Abc.ts",
+    "/components/abc/Context.ts",
+    "/components/abc/CreateEditModal.tsx",
+    "/components/abc/Form.tsx",
+    "/components/abc/LogsRenderer.tsx",
+  ].forEach((file) => expect(fs.existsSync(tmpobj.name + file)).toBe(true));
+
+  tmpobj.removeCallback();
+});
diff --git a/templates/react-native-v2/app/(tabs)/foos.tsx b/templates/react-native-v2/app/(tabs)/foos.tsx
new file mode 100644
index 00000000..9b6effe5
--- /dev/null
+++ b/templates/react-native-v2/app/(tabs)/foos.tsx
@@ -0,0 +1,118 @@
+import Main from "@/components/Main";
+import Navigation from "@/components/Navigation";
+import CreateEditModal from "@/components/{{{lc}}}/CreateEditModal";
+import LogsRenderer from "@/components/{{{lc}}}/LogsRenderer";
+import {{{ucf}}} from "@/lib/types/{{{ucf}}}";
+import { useLocalSearchParams, Link } from "expo-router";
+import { useEffect } from "react";
+import { useQuery } from '@tanstack/react-query'
+import { Pressable, ScrollView, Text, View } from "react-native";
+import { getAll } from "@/lib/api/{{{lc}}}Api";
+import { HydraResponse } from "@/lib/types/HydraResponse";
+import { {{{ucf}}}Context, {{{ucf}}}ContextData } from "@/components/{{{lc}}}/Context";
+import { useMercure } from "@/lib/hooks/mercure";
+import { useData } from "@/lib/hooks/data";
+import { useNotifications } from "@/lib/hooks/notifications";
+import { useModal } from "@/lib/hooks/modal";
+import { Icon } from "@/lib/utils/icons";
+
+export default function Books() {
+    const { page = '1' } = useLocalSearchParams<{ page: string }>();
+    const { id = undefined } = useLocalSearchParams<{ id: Nullable }>();
+
+    const { member, setMember, processMercureData, view, setView, currentData, setCurrentData } = useData<{{{ucf}}}>();
+    const { notifications, addNotification, clearNotifications } = useNotifications();
+    const { isModalEdit, isModalVisible, toggleEditModal, toggleCreateModal, setIsModalVisible } = useModal();
+
+    useMercure(['/{{{lc}}}s'], processMercureData);
+
+    const { isSuccess, data, isLoading, error } = useQuery>({
+        queryKey: ['getAll{{{ucf}}}s', page],
+        queryFn: () => getAll(page),
+    });
+
+    useEffect(() => {
+        if (isSuccess) {
+            setMember(data["{{{hydraPrefix}}}member"]);
+            setView(data['{{{hydraPrefix}}}view']);
+        }
+    }, [isSuccess, data]);
+
+    useEffect(() => {
+        if (!id) return;
+
+        const data = member.find(item => item["@id"].includes(id) == true);
+        if (data) {
+            setCurrentData(data);
+            toggleEditModal();
+        }
+    }, [member, id])
+
+    const providerValues: {{{ucf}}}ContextData = { notifications, addNotification, clearNotifications, isModalVisible, isModalEdit, setIsModalVisible, currentData };
+    const viewButtonStyle = { width: "5vw", minWidth: '100px', height: "100%" };
+
+    return (
+        
+            
+                {{{ucf}}}s List
+                 toggleCreateModal()}>
+                    Create
+                
+            
+            <{{{ucf}}}Context.Provider value={providerValues}>
+                
+                    
+                    
+                        {
+                            member && member.length < 1 &&
+                            
+                                {isLoading ? 'Loading data...' : 'No data found'}
+                            
+                        }
+                        {
+                            error &&
+                            
+                                {error.message}
+                            
+                        }
+                        {
+                            member && member.map((item: {{{ucf}}}) => (
+                                !item.deleted && 
+                                
+                                    
+                                        ID: {item['@id']}
+                                        {{#each fields}}
+                                            {{#if isReferences}}
+                                                {{{name}}}:
+                                                {item['{{{name}}}'].map((ref: any) => {ref})}
+                                            {{else if reference}}
+                                                {{{name}}}: {item["{{{name}}}"]}
+                                            {{else if isEmbeddeds}}
+                                                {{{name}}}:
+                                                {item['{{{name}}}'].map((emb: any) => {emb["@id"]})}
+                                            {{else if embedded}}
+                                                {{{name}}}: {item["{{{name}}}"]["@id"]}
+                                            {{else}}
+                                                {{{name}}}: {item["{{{name}}}"]}
+                                            {{/if}}
+                                        {{/each}}
+                                    
+                                    
+                                         {
+                                        setCurrentData(item);
+                                        toggleEditModal();
+                                        }}>
+                                            
+                                        
+                                    
+                                
+                            ))
+                        }
+                    
+                    
+                
+            {{{ucf}}}Context.Provider>
+            
+        
+    );
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/app/_layout.tsx.dist b/templates/react-native-v2/app/_layout.tsx.dist
new file mode 100644
index 00000000..8d0825ca
--- /dev/null
+++ b/templates/react-native-v2/app/_layout.tsx.dist
@@ -0,0 +1,44 @@
+import React from 'react';
+import { FontAwesome } from "@expo/vector-icons";
+import "../global.css";
+import { Tabs } from "expo-router";
+import {
+  QueryClient,
+  QueryClientProvider,
+} from '@tanstack/react-query'
+
+function TabBarIcon(props: {
+  name: React.ComponentProps['name'];
+  color: string;
+}) {
+  return ;
+}
+const iconMargin = { marginBottom: -3 }
+
+export default function Layout() {
+  const queryClient = new QueryClient();
+
+  return (
+    
+      
+        
+      
+    
+  )
+}
+
+const options = {
+  tabsContainer: {
+    headerShown: false,
+    tabBarShowLabel: false,
+  },
+  tabs: {
+    home: {
+      title: 'Accueil',
+      tabBarIcon: ({ color }) => ,
+    },
+  }
+};
\ No newline at end of file
diff --git a/templates/react-native-v2/components/ConfirmModal.tsx b/templates/react-native-v2/components/ConfirmModal.tsx
new file mode 100644
index 00000000..de16ac1e
--- /dev/null
+++ b/templates/react-native-v2/components/ConfirmModal.tsx
@@ -0,0 +1,29 @@
+import { Modal, Pressable, Text, View } from "react-native";
+
+export default function ConfirmModal(props: { isVisible: boolean, onDecline: Function, onAccept: Function }) {
+    const { isVisible, onDecline, onAccept } = props;
+
+    return (
+        
+            
+                
+                    Are you sure ?
+                    
+                         onAccept()}>
+                            Yes
+                        
+                         onDecline()}>
+                            Cancel
+                        
+                    
+                
+            
+        
+    )
+}
+
+const containerStyle = { height: '100%', width: '100%' }
\ No newline at end of file
diff --git a/templates/react-native-v2/components/Main.tsx b/templates/react-native-v2/components/Main.tsx
new file mode 100644
index 00000000..fa5fcb14
--- /dev/null
+++ b/templates/react-native-v2/components/Main.tsx
@@ -0,0 +1,16 @@
+import { View } from "react-native";
+
+export default function Main({ children }) {
+    return (
+        
+            {children}
+        
+    )
+}
+
+const styles = {
+    container: {
+        position: 'relative',
+        marginHorizontal: '3%',
+    }
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/components/Navigation.tsx b/templates/react-native-v2/components/Navigation.tsx
new file mode 100644
index 00000000..ff65f510
--- /dev/null
+++ b/templates/react-native-v2/components/Navigation.tsx
@@ -0,0 +1,64 @@
+import { HydraView } from "@/lib/types/HydraView";
+import { FontAwesome } from "@expo/vector-icons";
+import { useRouter } from "expo-router";
+import { Pressable, View } from "react-native";
+
+function NavigationIcon(props: {
+    name: React.ComponentProps['name'];
+    color: string;
+}) {
+    return ;
+}
+const iconMargin = { marginBottom: -3 }
+
+export default function Navigation(props: { view: HydraView }) {
+    const view = props.view;
+    if (!view) {
+        return null;
+    }
+
+    const router = useRouter();
+
+    const {
+        "{{{hydraPrefix}}}first": first,
+        "{{{hydraPrefix}}}previous": previous,
+        "{{{hydraPrefix}}}next": next,
+        "{{{hydraPrefix}}}last": last,
+    } = view;
+
+    return (
+        
+             {
+                if (first) router.navigate(first)
+            }}>
+                
+            
+
+             {
+                if (previous) router.navigate(previous)
+            }}>
+                
+            
+
+             {
+                if (next) router.navigate(next)
+            }}>
+                
+            
+
+             {
+                if (last) router.navigate(last)
+            }}>
+                
+            
+        
+    );
+}
+
+const styles = {
+    container: {
+        position: 'absolute',
+        bottom: 5,
+        minWidth: '100%'
+    }
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/components/foo/Context.ts b/templates/react-native-v2/components/foo/Context.ts
new file mode 100644
index 00000000..bd3f7f24
--- /dev/null
+++ b/templates/react-native-v2/components/foo/Context.ts
@@ -0,0 +1,15 @@
+import {{{ucf}}} from "@/lib/types/{{{ucf}}}";
+import { Log, addNotificationFunction, clearNotificationsFunction } from "@/lib/utils/Logs";
+import { createContext } from "react";
+
+export type {{{ucf}}}ContextData = {
+    notifications: Log[];
+    addNotification: addNotificationFunction,
+    clearNotifications: clearNotificationsFunction,
+    isModalVisible: boolean;
+    isModalEdit: boolean;
+    setIsModalVisible: (visible: boolean) => void;
+    currentData?: {{{ucf}}};
+}
+
+export const {{{ucf}}}Context = createContext<{{{ucf}}}ContextData>(null);
\ No newline at end of file
diff --git a/templates/react-native-v2/components/foo/CreateEditModal.tsx b/templates/react-native-v2/components/foo/CreateEditModal.tsx
new file mode 100644
index 00000000..f0cad875
--- /dev/null
+++ b/templates/react-native-v2/components/foo/CreateEditModal.tsx
@@ -0,0 +1,82 @@
+import { Modal, Pressable, Text, View } from "react-native";
+import Form from "./Form";
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import {{{ucf}}} from "@/lib/types/{{{ucf}}}";
+import { remove } from "@/lib/api/{{{lc}}}Api";
+import { useContext, useEffect, useState } from "react";
+import ConfirmModal from "../ConfirmModal";
+import { {{{ucf}}}Context } from "./Context";
+
+export default function CreateEditModal() {
+    const [requestDelete, setRequestDelete] = useState(false);
+    const queryClient = useQueryClient();
+
+    const context = useContext({{{ucf}}}Context);
+    const { addNotification, setIsModalVisible, isModalEdit, isModalVisible, currentData: data } = context;
+
+    const deleteMutation = useMutation({
+        mutationFn: (data: {{{ucf}}}) => remove(data),
+        onError: (error: string) => {
+            addNotification('error', error.toString());
+        },
+        onSuccess: (data) => {
+            if (data.ok) {
+                addNotification('success', 'The {{{lc}}} has been deleted');
+            } else {
+                addNotification('error', `An error occured while deleting the {{{lc}}} (${data.statusText})`);
+            }
+            queryClient.invalidateQueries({ queryKey: ['getAll{{{ucf}}}s'] });
+        },
+    });
+
+    useEffect(() => {
+        if (data && data.deleted) {
+            addNotification('error', `${data["@id"]} has been deleted by another user`);
+            setIsModalVisible(false);
+            setRequestDelete(false);
+        }
+    }, [JSON.stringify(data)])
+
+    const onAccept = () => {
+        deleteMutation.mutate(data);
+        setIsModalVisible(false);
+        setRequestDelete(false);
+    }
+
+    const onDecline = () => {
+        setRequestDelete(false);
+    }
+
+    return (
+        
+            
+                
+                    
+                    {isModalEdit ? `Edit {{{ucf}}}` : 'Create a new {{{ucf}}}'} ({ data && data['@id'] })
+                    
+                    {
+                        isModalEdit &&
+                         setRequestDelete(true)}>
+                            Delete
+                        
+                    }
+                     setIsModalVisible(false)}>
+                        Close
+                    
+                
+            
+        
+    )
+}
+
+const styles = {
+    container: { height: '80%', width: '100%', backgroundColor: '#e3e9e5' },
+    closeButton: { position: 'absolute', right: 5, top: 5 }
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/components/foo/Form.tsx b/templates/react-native-v2/components/foo/Form.tsx
new file mode 100644
index 00000000..cc91008f
--- /dev/null
+++ b/templates/react-native-v2/components/foo/Form.tsx
@@ -0,0 +1,109 @@
+import {{{ucf}}} from "@/lib/types/{{{ucf}}}";
+import { useContext, useState } from "react";
+import { Controller, SubmitErrorHandler, useForm } from "react-hook-form";
+import { Pressable, Text, TextInput, View } from "react-native";
+import { SafeAreaView } from "react-native-safe-area-context";
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { create, update } from "@/lib/api/{{{lc}}}Api";
+import { {{{ucf}}}Context } from "./Context";
+
+export default function Form() {
+    const [errors, setErrors] = useState([]);
+    const queryClient = useQueryClient();
+
+    const context = useContext({{{ucf}}}Context);
+    const { addNotification, isModalEdit, setIsModalVisible, currentData: data } = context;
+
+    const queryFn = isModalEdit ? update : create;
+
+    const mutation = useMutation({
+        mutationFn: (data: {{{ucf}}}) => queryFn(data),
+        onError: (error) => {
+            addNotification('error', error.toString());
+        },
+        onSuccess: (data) => {
+            if (data.ok) {
+                addNotification('success', `The {{{lc}}} has been ${isModalEdit ? 'updated' : 'created'}`);
+            } else {
+                addNotification('error', `An error occured while ${isModalEdit ? 'updating' : 'creating'} the {{{lc}}} (${data.statusText})`);
+            }
+            queryClient.invalidateQueries({ queryKey: ['getAll'] });
+        }
+    });
+
+    let initValues: {{{ucf}}} = (isModalEdit && data) ? data : {
+        '@id': '',
+        {{#each formFields}}
+            {{{name}}}: {{#if (compare type "==" "number")}}0{{else}}''{{/if}},
+        {{/each}}
+    }
+
+    const { control, handleSubmit, reset } = useForm<{{{ucf}}}>({
+        defaultValues: initValues
+    });
+
+    const onSubmit = (data: {{{ucf}}}) => {
+        intParser(data);
+        mutation.mutate(data);
+        setIsModalVisible(false);
+        reset();
+    };
+
+    const intParser = (data: {{{ucf}}}) => {
+        Object.keys(data).forEach(key => {
+            if ((typeof initValues[key] == "number") && !isNaN(parseInt(data[key]))) {
+                data[key] = parseInt(data[key]);
+            }
+        });
+    }
+
+    const onError: SubmitErrorHandler<{{{ucf}}}> = (errors, e) => {
+        setErrors(Object.keys(errors));
+    }
+
+    return (
+        
+            
+                {errors.length > 0 &&
+                    
+                        
+                            Field{errors.length > 1 ? "s" : ""} "{errors.join(', ')}" {errors.length > 1 ? "are" : "is"} empty
+                        
+                    
+                }
+                {{#each formFields}}
+                 (
+                        
+                            {{{name}}} :
+                            
+                        
+                    )}
+                    name="{{{name}}}"
+                    {{#if required}}rules={fieldRequired}{{/if}}
+                />
+                {{/each}}
+                
+                    {isModalEdit ? 'Edit' : 'Create'}
+                
+            
+        
+    );
+}
+
+const fieldRequired = {
+    required: true
+}
+
+const styles = {
+    textInput: { minWidth: 200 }
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/components/foo/LogsRenderer.tsx b/templates/react-native-v2/components/foo/LogsRenderer.tsx
new file mode 100644
index 00000000..41d9d69c
--- /dev/null
+++ b/templates/react-native-v2/components/foo/LogsRenderer.tsx
@@ -0,0 +1,47 @@
+import { Log, LogType } from "@/lib/utils/Logs";
+import { useContext, useMemo } from "react";
+import { Pressable, Text, View } from "react-native";
+import { {{{ucf}}}Context } from "./Context";
+
+export default function LogsRenderer() {
+    const context = useContext({{{ucf}}}Context);
+    const { notifications, clearNotifications } = context;
+
+    const filterLogs = (type: keyof LogType) => {
+        return notifications.filter(log => log.type == type);
+    }
+
+    const errors = useMemo(() => filterLogs("error"), [notifications]);
+    const successes = useMemo(() => filterLogs("success"), [notifications]);
+
+    return (
+        
+            {
+                errors.length > 0 &&
+                
+                    
+                        {errors.map((error: Log, index: number) => (
+                            - {error.message}
+                        ))}
+                    
+                     clearNotifications('error')}>
+                        X
+                    
+                
+            }
+            {
+                successes.length > 0 &&
+                
+                    
+                        {successes.map((success: Log, index: number) => (
+                            - {success.message}
+                        ))}
+                    
+                     clearNotifications('success')}>
+                        X
+                    
+                
+            }
+        
+    )
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/api/fooApi.ts b/templates/react-native-v2/lib/api/fooApi.ts
new file mode 100644
index 00000000..7865ca6a
--- /dev/null
+++ b/templates/react-native-v2/lib/api/fooApi.ts
@@ -0,0 +1,49 @@
+import { ENTRYPOINT } from '@/config/entrypoint';
+import {{{ucf}}} from '../types/{{{ucf}}}';
+
+const ENDPOINT = `{{{lc}}}s`;
+
+export function getAll(pageId: string) {
+    let page = parseInt(pageId);
+
+    if (page < 1 || isNaN(page)) {
+        page = 1;
+    }
+
+    return fetch(`${ENTRYPOINT}/${ENDPOINT}?page=${page}`).then(res => res.json());
+};
+
+export function update(data: {{{ucf}}}): Promise {
+    return fetch(
+        `${ENTRYPOINT}${data['@id']}`,
+        {
+            method: 'PUT',
+            headers: {
+                "Content-Type": "application/json",
+            },
+            body: JSON.stringify(data)
+        }
+    )
+}
+
+export function create(data: {{{ucf}}}): Promise {
+    return fetch(
+        `${ENTRYPOINT}/${ENDPOINT}`,
+        {
+            method: 'POST',
+            headers: {
+                "Content-Type": "application/json",
+            },
+            body: JSON.stringify(data)
+        }
+    )
+}
+
+export function remove(data: {{{ucf}}}): Promise {
+    return fetch(
+        `${ENTRYPOINT}${data['@id']}`,
+        {
+            method: 'DELETE',
+        }
+    )
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/hooks/data.ts b/templates/react-native-v2/lib/hooks/data.ts
new file mode 100644
index 00000000..90c09a8d
--- /dev/null
+++ b/templates/react-native-v2/lib/hooks/data.ts
@@ -0,0 +1,36 @@
+import { useCallback, useState } from "react";
+import ApiResource from "../types/ApiResource";
+import { HydraView } from "../types/HydraView";
+
+type useDataType = {
+    member: T[];
+    setMember: (member: T[]) => void,
+    processMercureData: (member: T) => void,
+    view: HydraView;
+    setView: (view: HydraView) => void;
+    currentData: Nullable;
+    setCurrentData: (data: T) => void;
+}
+
+export const useData = (): useDataType => {
+    const [member, setMember] = useState([]);
+    const [view, setView] = useState({});
+    const [currentData, setCurrentData] = useState>(undefined);
+
+    const processMercureData = useCallback((data: T) => {
+        const currentMember = member.find(item => item["@id"] == data["@id"]);
+
+        if (Object.keys(data).length == 1) {
+            data.deleted = true;
+        }
+
+        if (currentMember) {
+            Object.assign(currentMember, data);
+            setMember([...member]); // force re-render
+        } else {
+            setMember([...member, data]);
+        }
+    }, [member]);
+
+    return { member, setMember, processMercureData, view, setView, currentData, setCurrentData };
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/hooks/mercure.ts b/templates/react-native-v2/lib/hooks/mercure.ts
new file mode 100644
index 00000000..06cdb4b4
--- /dev/null
+++ b/templates/react-native-v2/lib/hooks/mercure.ts
@@ -0,0 +1,33 @@
+import { useEffect, useState } from "react";
+import ApiResource from "../types/ApiResource";
+import { ENTRYPOINT } from "@/config/entrypoint";
+import { extractHubURL, mercureSubscribe } from "../utils/mercure";
+
+type useMercureType = {
+    hubURL: Nullable;
+    eventSource: Nullable;
+}
+
+export const useMercure = (topics: string[], setData: (data: T) => void): useMercureType => {
+    const [hubURL, setHubURL] = useState>(undefined);
+    const [eventSource, setEventSource] = useState>(undefined);
+    if (topics.length < 1) return { hubURL, eventSource };
+
+    useEffect(() => {
+        fetch(`${ENTRYPOINT}${topics[0]}`)
+            .then(res => {
+                const extractedUrl = extractHubURL(res);
+                if (extractedUrl) {
+                    setHubURL(extractedUrl.href);
+                }
+            });
+
+        if (hubURL) {
+            setEventSource(mercureSubscribe(new URL(hubURL), topics, setData));
+        }
+
+        return () => eventSource && eventSource.close();
+    }, [hubURL, setData]);
+
+    return { hubURL, eventSource };
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/hooks/modal.ts b/templates/react-native-v2/lib/hooks/modal.ts
new file mode 100644
index 00000000..d9ee8957
--- /dev/null
+++ b/templates/react-native-v2/lib/hooks/modal.ts
@@ -0,0 +1,27 @@
+import { useState } from "react";
+
+type useModalType = {
+    isModalVisible: boolean;
+    isModalEdit: boolean;
+    toggleEditModal: () => void;
+    toggleCreateModal: () => void;
+    setIsModalEdit: (state: boolean) => void;
+    setIsModalVisible: (state: boolean) => void;
+}
+
+export const useModal = (): useModalType => {
+    const [isModalVisible, setIsModalVisible] = useState(false);
+    const [isModalEdit, setIsModalEdit] = useState(false);
+
+    const toggleEditModal = () => {
+        setIsModalVisible(true);
+        setIsModalEdit(true);
+    };
+
+    const toggleCreateModal = () => {
+        setIsModalVisible(true);
+        setIsModalEdit(false);
+    }
+
+    return { isModalEdit, isModalVisible, toggleCreateModal, toggleEditModal, setIsModalEdit, setIsModalVisible }
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/hooks/notifications.ts b/templates/react-native-v2/lib/hooks/notifications.ts
new file mode 100644
index 00000000..d9505052
--- /dev/null
+++ b/templates/react-native-v2/lib/hooks/notifications.ts
@@ -0,0 +1,31 @@
+import { useEffect, useState } from "react";
+import { Log, LogType, addNotificationFunction, clearNotificationsFunction } from "../utils/Logs";
+
+type useNotificationsType = {
+    notifications: Log[];
+    setNotifications: (notifications: Log[]) => void;
+    addNotification: addNotificationFunction;
+    clearNotifications: clearNotificationsFunction;
+}
+
+export const useNotifications = (): useNotificationsType => {
+    const [notifications, setNotifications] = useState([]);
+    const TIMEOUT_MS = 5000; // 5 secondes
+
+    useEffect(() => {
+        const timeoutId = setTimeout(() => setNotifications([]), TIMEOUT_MS);
+
+        return () => clearTimeout(timeoutId);
+    }, [notifications]);
+
+
+    const addNotification = (type: keyof LogType, message: string) => {
+        setNotifications([...notifications, new Log(type, message)]);
+    };
+
+    const clearNotifications = (type: keyof LogType) => {
+        setNotifications([...notifications.filter(log => log.type !== type)]);
+    };
+
+    return { notifications, setNotifications, addNotification, clearNotifications };
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/types/ApiResource.ts b/templates/react-native-v2/lib/types/ApiResource.ts
new file mode 100644
index 00000000..aff6788e
--- /dev/null
+++ b/templates/react-native-v2/lib/types/ApiResource.ts
@@ -0,0 +1,4 @@
+export default interface ApiResource {
+    "@id": string;
+    deleted?: boolean;
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/types/HydraResponse.ts b/templates/react-native-v2/lib/types/HydraResponse.ts
new file mode 100644
index 00000000..5a4f1119
--- /dev/null
+++ b/templates/react-native-v2/lib/types/HydraResponse.ts
@@ -0,0 +1,7 @@
+import ApiResource from "./ApiResource";
+import { HydraView } from "./HydraView";
+
+export interface HydraResponse {
+    '{{{hydraPrefix}}}member'?: Array;
+    '{{{hydraPrefix}}}view'?: HydraView;
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/types/HydraView.ts b/templates/react-native-v2/lib/types/HydraView.ts
new file mode 100644
index 00000000..5c874e2c
--- /dev/null
+++ b/templates/react-native-v2/lib/types/HydraView.ts
@@ -0,0 +1,6 @@
+export interface HydraView {
+    '{{{hydraPrefix}}}first'?: string;
+    '{{{hydraPrefix}}}last'?: string;
+    '{{{hydraPrefix}}}previous'?: string;
+    '{{{hydraPrefix}}}next'?: string;
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/types/foo.ts b/templates/react-native-v2/lib/types/foo.ts
new file mode 100644
index 00000000..ebb9682a
--- /dev/null
+++ b/templates/react-native-v2/lib/types/foo.ts
@@ -0,0 +1,7 @@
+import ApiResource from "./ApiResource";
+
+export default interface {{{ucf}}} extends ApiResource {
+    {{#each fields}}
+        {{#if readonly}}readonly{{/if}} {{{name}}}?: {{#if (compare type "==" "Date")}}string{{else}}{{{type}}}{{/if}};
+    {{/each}}
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/utils/Logs.ts b/templates/react-native-v2/lib/utils/Logs.ts
new file mode 100644
index 00000000..5dec4394
--- /dev/null
+++ b/templates/react-native-v2/lib/utils/Logs.ts
@@ -0,0 +1,17 @@
+export interface LogType {
+    "error": string;
+    "success": string;
+}
+
+export class Log {
+    type: keyof LogType;
+    message: string;
+
+    constructor(type: keyof LogType, message: string) {
+        this.type = type;
+        this.message = message;
+    }
+}
+
+export type addNotificationFunction = (type: keyof LogType, message: string) => void;
+export type clearNotificationsFunction = (type: keyof LogType) => void;
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/utils/icons.tsx b/templates/react-native-v2/lib/utils/icons.tsx
new file mode 100644
index 00000000..0ec42e62
--- /dev/null
+++ b/templates/react-native-v2/lib/utils/icons.tsx
@@ -0,0 +1,13 @@
+import { FontAwesome } from "@expo/vector-icons";
+
+type IconProps = {
+    size?: number;
+    name: React.ComponentProps['name'];
+    color?: string;
+}
+
+export function Icon({ size = 32, name, color = "black" }: IconProps) {
+    const iconMargin = { marginBottom: -3 }
+
+    return ;
+}
\ No newline at end of file
diff --git a/templates/react-native-v2/lib/utils/mercure.ts b/templates/react-native-v2/lib/utils/mercure.ts
new file mode 100644
index 00000000..9c159b6d
--- /dev/null
+++ b/templates/react-native-v2/lib/utils/mercure.ts
@@ -0,0 +1,33 @@
+import { ENTRYPOINT } from "@/config/entrypoint";
+import type ApiResource from "@/lib/types/ApiResource";
+
+export const mercureSubscribe = (
+    hubURL: URL,
+    topics: string[],
+    setData: (data: T) => void
+): EventSource => {
+    const url = new URL(hubURL);
+
+    topics.forEach((topic) =>
+        url.searchParams.append("topic", new URL(topic, ENTRYPOINT).toString())
+    );
+
+    const eventSource = new EventSource(url.toString());
+
+    eventSource.addEventListener("message", (event) => {
+        setData(JSON.parse(event.data));
+    });
+
+    return eventSource;
+};
+
+export const extractHubURL = (response: Response): URL | undefined => {
+    const linkHeader = response.headers.get("Link");
+    if (!linkHeader) return undefined;
+
+    const matches = linkHeader.match(
+        /<([^>]+)>;\s+rel=(?:mercure|"[^"]*mercure[^"]*")/
+    );
+
+    return matches && matches[1] ? new URL(matches[1], ENTRYPOINT) : undefined;
+};
\ No newline at end of file